diff --git a/.eslintrc.yml b/.eslintrc.yml index b6b9f62f..b0414210 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -6,10 +6,14 @@ plugins: overrides: - files: '**/*.md' processor: 'markdown/markdown' +env: + es2022: true + node: true rules: eol-last: error eqeqeq: ["error", "always", { "null": "ignore" }] indent: ["error", 2, { "MemberExpression": "off", "SwitchCase": 1 }] no-mixed-spaces-and-tabs: error no-trailing-spaces: error + no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }] one-var: ["error", { "initialized": "never" }] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff3bce76..0dfdd660 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,236 +1,105 @@ name: ci on: -- pull_request -- push + push: + branches: + - 'v2' + paths-ignore: + - '*.md' + pull_request: + branches: + - 'v2' + paths-ignore: + - '*.md' + +permissions: + contents: read + +# Cancel in progress workflows +# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run +concurrency: + group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm install --ignore-scripts --include=dev + + - name: Run lint + run: npm run lint + test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - name: - - Node.js 0.8 - - Node.js 0.10 - - Node.js 0.12 - - io.js 1.x - - io.js 2.x - - io.js 3.x - - Node.js 4.x - - Node.js 5.x - - Node.js 6.x - - Node.js 7.x - - Node.js 8.x - - Node.js 9.x - - Node.js 10.x - - Node.js 11.x - - Node.js 12.x - - Node.js 13.x - - Node.js 14.x - - Node.js 15.x - - Node.js 16.x - - Node.js 17.x - - Node.js 18.x - - Node.js 19.x - - Node.js 20.x - - Node.js 21.x - - include: - - name: Node.js 0.8 - node-version: "0.8" - npm-i: mocha@2.5.3 supertest@1.1.0 - npm-rm: nyc - - - name: Node.js 0.10 - node-version: "0.10" - npm-i: mocha@2.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: Node.js 0.12 - node-version: "0.12" - npm-i: mocha@2.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 1.x - node-version: "1.8" - npm-i: mocha@2.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 2.x - node-version: "2.5" - npm-i: mocha@2.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: io.js 3.x - node-version: "3.3" - npm-i: mocha@2.5.3 nyc@10.3.2 supertest@2.0.0 - - - name: Node.js 4.x - node-version: "4.9" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 5.x - node-version: "5.12" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 6.x - node-version: "6.17" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - - - name: Node.js 7.x - node-version: "7.10" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - - - name: Node.js 8.x - node-version: "8.17" - npm-i: mocha@7.2.0 nyc@14.1.1 - - - name: Node.js 9.x - node-version: "9.11" - npm-i: mocha@7.2.0 nyc@14.1.1 - - - name: Node.js 10.x - node-version: "10.24" - npm-i: mocha@8.4.0 - - - name: Node.js 11.x - node-version: "11.15" - npm-i: mocha@8.4.0 - - - name: Node.js 12.x - node-version: "12.22" - npm-i: mocha@9.2.2 - - - name: Node.js 13.x - node-version: "13.14" - npm-i: mocha@9.2.2 - - - name: Node.js 14.x - node-version: "14.21" - - - name: Node.js 15.x - node-version: "15.14" - - - name: Node.js 16.x - node-version: "16.20" - - - name: Node.js 17.x - node-version: "17.9" - - - name: Node.js 18.x - node-version: "18.19" - - - name: Node.js 19.x - node-version: "19.9" - - - name: Node.js 20.x - node-version: "20.11" - - - name: Node.js 21.x - node-version: "21.6" - + node-version: [18, 19, 20, 21, 22, 23] + # Node.js release schedule: https://nodejs.org/en/about/releases/ + name: Test - Node.js ${{ matrix.node-version }} steps: - - uses: actions/checkout@v4 - - - name: Install Node.js ${{ matrix.node-version }} - shell: bash -eo pipefail -l {0} - run: | - nvm install --default ${{ matrix.node-version }} - if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then - nvm install --alias=npm 0.10 - nvm use ${{ matrix.node-version }} - sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")" - npm config set strict-ssl false - fi - dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - - - name: Configure npm - run: | - if [[ "$(npm config get package-lock)" == "true" ]]; then - npm config set package-lock false - else - npm config set shrinkwrap false - fi - - - name: Remove npm module(s) ${{ matrix.npm-rm }} - run: npm rm --silent --save-dev ${{ matrix.npm-rm }} - if: matrix.npm-rm != '' - - - name: Install npm module(s) ${{ matrix.npm-i }} - run: npm install --save-dev ${{ matrix.npm-i }} - if: matrix.npm-i != '' - - - name: Setup Node.js version-specific dependencies - shell: bash - run: | - # eslint for linting - # - remove on Node.js < 12 - if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then - node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ - grep -E '^eslint(-|$)' | \ - sort -r | \ - xargs -n1 npm rm --silent --save-dev - fi - - - name: Install Node.js dependencies - run: npm install - - - name: List environment - id: list_env - shell: bash - run: | - echo "node@$(node -v)" - echo "npm@$(npm -v)" - npm -s ls ||: - (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" - - - name: Run tests - shell: bash - run: | - if npm -ps ls nyc | grep -q nyc; then - npm run test-ci - cp coverage/lcov.info "coverage/${{ matrix.node-version }}.lcov" - else - npm test - fi - - - name: Lint code - if: steps.list_env.outputs.eslint != '' - run: npm run lint - - - name: Collect code coverage - if: steps.list_env.outputs.nyc != '' - run: | - if [[ -d ./coverage ]]; then - mv ./coverage "./${{ matrix.node-version }}" - mkdir ./coverage - mv "./${{ matrix.node-version }}" "./coverage/${{ matrix.node-version }}" - fi - - - name: Upload code coverage - uses: actions/upload-artifact@v4 - if: steps.list_env.outputs.nyc != '' - with: - name: coverage-${{ matrix.node-version }} - path: "./coverage/${{ matrix.node-version }}" - retention-days: 1 + - uses: actions/checkout@v4 + with: + persist-credentials: false - coverage: - needs: test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Configure npm loglevel + run: | + npm config set loglevel error + shell: bash + + - name: Install dependencies + run: npm install - - name: Install lcov - shell: bash - run: sudo apt-get -y install lcov - - name: Collect coverage reports - uses: actions/download-artifact@v4 - with: - path: ./coverage + - name: Run tests + shell: bash + run: npm run test-ci - - name: Merge coverage reports - shell: bash - run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info + - name: Upload code coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-node-${{ matrix.node-version }} + path: ./coverage/lcov.info + retention-days: 1 - - name: Upload coverage report - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} + coverage: + needs: test + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + steps: + - uses: actions/checkout@v4 + + - name: Install lcov + shell: bash + run: sudo apt-get -y install lcov + + - name: Collect coverage reports + uses: actions/download-artifact@v4 + with: + path: ./coverage + pattern: coverage-node-* + + - name: Merge coverage reports + shell: bash + run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info + + - name: Upload coverage report + uses: coverallsapp/github-action@v2 + with: + file: ./lcov.info \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..9cf94950 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index 57c68d3a..238498ec 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,9 @@ +unreleased +========== + + * Replace `uid-safe` dependency with built-in `crypto.randomBytes` for session ID generation + - Session IDs keep the same format as before (32-character base64url strings) + 1.18.1 / 2024-10-08 ========== diff --git a/README.md b/README.md index 65a37e63..1e30b86b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,10 @@ $ npm install express-session ## API ```js -var session = require('express-session') +const session = require('express-session') + +const app = express() +app.use(session(/* options */)) ``` ### session(options) @@ -246,8 +249,7 @@ parallel requests to your server and changes made to the session in one request may get overwritten when the other request ends, even if it made no changes (this behavior also depends on what store you're using). -The default value is `true`, but using the default has been deprecated, -as the default will change in the future. Please research into this setting +The default value is `false`. Please research into this setting and choose what is appropriate to your use-case. Typically, you'll want `false`. diff --git a/index.js b/index.js index d41b2378..09a86c2f 100644 --- a/index.js +++ b/index.js @@ -13,7 +13,6 @@ * @private */ -var Buffer = require('safe-buffer').Buffer var cookie = require('cookie'); var crypto = require('crypto') var debug = require('debug')('express-session'); @@ -21,7 +20,6 @@ var deprecate = require('depd')('express-session'); var onHeaders = require('on-headers') var parseUrl = require('parseurl'); var signature = require('cookie-signature') -var uid = require('uid-safe').sync var Cookie = require('./session/cookie') var MemoryStore = require('./session/memory') @@ -56,16 +54,6 @@ var warning = 'Warning: connect.session() MemoryStore is not\n' + 'designed for a production environment, as it will leak\n' + 'memory, and will not scale past a single process.'; -/** - * Node.js 0.8+ async implementation. - * @private - */ - -/* istanbul ignore next */ -var defer = typeof setImmediate === 'function' - ? setImmediate - : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) } - /** * Setup session store with the given `options`. * @@ -112,15 +100,14 @@ function session(options) { var saveUninitializedSession = opts.saveUninitialized // get the cookie signing secret - var secret = opts.secret + var secrets = opts.secret if (typeof generateId !== 'function') { throw new TypeError('genid option must be a function'); } if (resaveSession === undefined) { - deprecate('undefined resave option; provide resave option'); - resaveSession = true; + resaveSession = false; } if (saveUninitializedSession === undefined) { @@ -135,16 +122,16 @@ function session(options) { // TODO: switch to "destroy" on next major var unsetDestroy = opts.unset === 'destroy' - if (Array.isArray(secret) && secret.length === 0) { + if (Array.isArray(secrets) && secrets.length === 0) { throw new TypeError('secret option array must contain one or more strings'); } - if (secret && !Array.isArray(secret)) { - secret = [secret]; + if (secrets && !Array.isArray(secrets)) { + secrets = [secrets]; } - if (!secret) { - deprecate('req.secret; provide secret option'); + if (!secrets) { + throw new Error('secret option required for sessions'); } // notify user that this store is not @@ -199,16 +186,6 @@ function session(options) { return } - // ensure a secret is available or bail - if (!secret && !req.secret) { - next(new Error('secret option required for sessions')); - return; - } - - // backwards compatibility for signed cookies - // req.secret is passed from the cookie parser middleware - var secrets = secret || [req.secret]; - var originalHash; var originalId; var savedHash; @@ -228,6 +205,7 @@ function session(options) { } if (!shouldSetCookie(req)) { + debug('should not set cookie'); return; } @@ -240,6 +218,7 @@ function session(options) { if (!touched) { // touch session req.session.touch() + debug('touch session'); touched = true } @@ -247,7 +226,7 @@ function session(options) { try { setcookie(res, name, req.sessionID, secrets[0], req.session.cookie.data) } catch (err) { - defer(next, err) + setImmediate(next, err) } }); @@ -280,8 +259,8 @@ function session(options) { return ret; } - if (!res._header) { - res._implicitHeader() + if (!res.headersSent) { + res.writeHead(res.statusCode); } if (chunk == null) { @@ -317,7 +296,7 @@ function session(options) { debug('destroying'); store.destroy(req.sessionID, function ondestroy(err) { if (err) { - defer(next, err); + setImmediate(next, err); } debug('destroyed'); @@ -342,7 +321,7 @@ function session(options) { if (shouldSave(req)) { req.session.save(function onsave(err) { if (err) { - defer(next, err); + setImmediate(next, err); } writeend(); @@ -354,7 +333,7 @@ function session(options) { debug('touching'); store.touch(req.sessionID, req.session, function ontouch(err) { if (err) { - defer(next, err); + setImmediate(next, err); } debug('touched'); @@ -523,8 +502,8 @@ function session(options) { * @private */ -function generateSessionId(sess) { - return uid(24); +function generateSessionId() { + return crypto.randomBytes(24).toString('base64url'); } /** @@ -559,37 +538,6 @@ function getcookie(req, name, secrets) { } } - // back-compat read from cookieParser() signedCookies data - if (!val && req.signedCookies) { - val = req.signedCookies[name]; - - if (val) { - deprecate('cookie should be available in req.headers.cookie'); - } - } - - // back-compat read from cookieParser() cookies data - if (!val && req.cookies) { - raw = req.cookies[name]; - - if (raw) { - if (raw.substr(0, 2) === 's:') { - val = unsigncookie(raw.slice(2), secrets); - - if (val) { - deprecate('cookie should be available in req.headers.cookie'); - } - - if (val === false) { - debug('cookie signature invalid'); - val = undefined; - } - } else { - debug('cookie unsigned') - } - } - } - return val; } @@ -603,14 +551,8 @@ function getcookie(req, name, secrets) { function hash(sess) { // serialize - var str = JSON.stringify(sess, function (key, val) { - // ignore sess.cookie property - if (this === sess && key === 'cookie') { - return - } - - return val - }) + const { cookie, ...sessWithoutCookie } = sess; + const str = JSON.stringify(sessWithoutCookie); // hash return crypto @@ -631,17 +573,21 @@ function hash(sess) { function issecure(req, trustProxy) { // socket is https server if (req.connection && req.connection.encrypted) { + debug('connection encrypted'); return true; } // do not trust proxy if (trustProxy === false) { + debug('proxy untrusted'); return false; } // no explicit trust; try req.secure from express if (trustProxy !== true) { - return req.secure === true + var reqSecure = req.secure === true + debug('request %s', reqSecure ? 'secure' : 'insecure'); + return reqSecure } // read the proto from x-forwarded-proto header @@ -651,7 +597,9 @@ function issecure(req, trustProxy) { ? header.substr(0, index).toLowerCase().trim() : header.toLowerCase().trim() - return proto === 'https'; + var protoSecure = proto === 'https'; + debug('protocol %s', protoSecure ? 'secure' : 'insecure'); + return protoSecure; } /** diff --git a/package.json b/package.json index 79cb1b4c..1a776b00 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,10 @@ "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", - "debug": "2.6.9", + "debug": "4.4.3", "depd": "~2.0.0", "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" + "parseurl": "~1.3.3" }, "devDependencies": { "after": "0.8.2", @@ -35,11 +33,11 @@ "index.js" ], "engines": { - "node": ">= 0.8.0" + "node": ">=18" }, "scripts": { "lint": "eslint . && node ./scripts/lint-readme.js", - "test": "./test/support/gencert.sh && mocha --require test/support/env --check-leaks --bail --no-exit --reporter spec test/", + "test": "./test/support/gencert.sh && mocha --require test/support/env --check-leaks --no-exit --reporter spec test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc npm test", "version": "node scripts/version-history.js && git add HISTORY.md" diff --git a/session/memory.js b/session/memory.js index 11ed686c..2008939a 100644 --- a/session/memory.js +++ b/session/memory.js @@ -16,16 +16,6 @@ var Store = require('./store') var util = require('util') -/** - * Shim setImmediate for node.js < 0.10 - * @private - */ - -/* istanbul ignore next */ -var defer = typeof setImmediate === 'function' - ? setImmediate - : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) } - /** * Module exports. */ @@ -68,7 +58,7 @@ MemoryStore.prototype.all = function all(callback) { } } - callback && defer(callback, null, sessions) + callback && setImmediate(callback, null, sessions) } /** @@ -80,7 +70,7 @@ MemoryStore.prototype.all = function all(callback) { MemoryStore.prototype.clear = function clear(callback) { this.sessions = Object.create(null) - callback && defer(callback) + callback && setImmediate(callback) } /** @@ -92,7 +82,7 @@ MemoryStore.prototype.clear = function clear(callback) { MemoryStore.prototype.destroy = function destroy(sessionId, callback) { delete this.sessions[sessionId] - callback && defer(callback) + callback && setImmediate(callback) } /** @@ -104,7 +94,7 @@ MemoryStore.prototype.destroy = function destroy(sessionId, callback) { */ MemoryStore.prototype.get = function get(sessionId, callback) { - defer(callback, null, getSession.call(this, sessionId)) + setImmediate(callback, null, getSession.call(this, sessionId)) } /** @@ -118,7 +108,7 @@ MemoryStore.prototype.get = function get(sessionId, callback) { MemoryStore.prototype.set = function set(sessionId, session, callback) { this.sessions[sessionId] = JSON.stringify(session) - callback && defer(callback) + callback && setImmediate(callback) } /** @@ -153,7 +143,7 @@ MemoryStore.prototype.touch = function touch(sessionId, session, callback) { this.sessions[sessionId] = JSON.stringify(currentSession) } - callback && defer(callback) + callback && setImmediate(callback) } /** diff --git a/test/session.js b/test/session.js index 7bf3e51f..085eaff3 100644 --- a/test/session.js +++ b/test/session.js @@ -35,22 +35,6 @@ describe('session()', function(){ .expect(200, done) }) - it('should error without secret', function(done){ - request(createServer({ secret: undefined })) - .get('/') - .expect(500, /secret.*required/, done) - }) - - it('should get secret from req.secret', function(done){ - function setup (req) { - req.secret = 'keyboard cat' - } - - request(createServer(setup, { secret: undefined })) - .get('/') - .expect(200, '', done) - }) - it('should create a new session', function (done) { var store = new session.MemoryStore() var server = createServer({ store: store }, function (req, res) { @@ -952,7 +936,7 @@ describe('session()', function(){ }); describe('resave option', function(){ - it('should default to true', function(done){ + it('should default to false', function(done){ var store = new session.MemoryStore() var server = createServer({ store: store }, function (req, res) { req.session.user = 'bob' @@ -962,14 +946,15 @@ describe('session()', function(){ request(server) .get('/') .expect(shouldSetSessionInStore(store)) - .expect(200, function(err, res){ - if (err) return done(err); + .expect(200, function (err, res) { + if (err) return done(err) + request(server) - .get('/') - .set('Cookie', cookie(res)) - .expect(shouldSetSessionInStore(store)) - .expect(200, done); - }); + .get('/') + .set('Cookie', cookie(res)) + .expect(shouldNotSetSessionInStore(store)) + .expect(200, done) + }) }); describe('when true', function () { @@ -1194,6 +1179,12 @@ describe('session()', function(){ }); describe('secret option', function () { + it('should reject without secret',function () { + for (const secret of [undefined, null, '', false]) { + assert.throws(session.bind(null, { secret }), /secret option required for sessions/) + } + }) + it('should reject empty arrays', function () { assert.throws(createServer.bind(null, { secret: [] }), /secret option array/); }) @@ -2310,10 +2301,10 @@ describe('session()', function(){ }) describe('cookieParser()', function () { - it('should read from req.cookies', function(done){ + it('shouldn\'t read from req.cookies', function(done){ var app = express() .use(cookieParser()) - .use(function(req, res, next){ req.headers.cookie = 'foo=bar'; next() }) + .use(function(req, res, next){ delete req.headers.cookie; next() }) .use(createSession()) .use(function(req, res, next){ req.session.count = req.session.count || 0 @@ -2328,56 +2319,11 @@ describe('session()', function(){ request(app) .get('/') .set('Cookie', cookie(res)) - .expect(200, '2', done) - }) - }) - - it('should reject unsigned from req.cookies', function(done){ - var app = express() - .use(cookieParser()) - .use(function(req, res, next){ req.headers.cookie = 'foo=bar'; next() }) - .use(createSession({ key: 'sessid' })) - .use(function(req, res, next){ - req.session.count = req.session.count || 0 - req.session.count++ - res.end(req.session.count.toString()) - }) - - request(app) - .get('/') - .expect(200, '1', function (err, res) { - if (err) return done(err) - request(app) - .get('/') - .set('Cookie', 'sessid=' + sid(res)) - .expect(200, '1', done) - }) - }) - - it('should reject invalid signature from req.cookies', function(done){ - var app = express() - .use(cookieParser()) - .use(function(req, res, next){ req.headers.cookie = 'foo=bar'; next() }) - .use(createSession({ key: 'sessid' })) - .use(function(req, res, next){ - req.session.count = req.session.count || 0 - req.session.count++ - res.end(req.session.count.toString()) - }) - - request(app) - .get('/') - .expect(200, '1', function (err, res) { - if (err) return done(err) - var val = cookie(res).replace(/...\./, '.') - request(app) - .get('/') - .set('Cookie', val) .expect(200, '1', done) }) }) - it('should read from req.signedCookies', function(done){ + it('shouldn\'t read from req.signedCookies', function(done){ var app = express() .use(cookieParser('keyboard cat')) .use(function(req, res, next){ delete req.headers.cookie; next() }) @@ -2395,7 +2341,7 @@ describe('session()', function(){ request(app) .get('/') .set('Cookie', cookie(res)) - .expect(200, '2', done) + .expect(200, '1', done) }) }) }) @@ -2430,7 +2376,7 @@ function createRequestListener(opts, fn) { var server = this _session(req, res, function (err) { - if (err && !res._header) { + if (err && !res.headersSent) { res.statusCode = err.status || 500 res.end(err.message) return diff --git a/test/support/smart-store.js b/test/support/smart-store.js index 8b224fdd..5e1bfb2f 100644 --- a/test/support/smart-store.js +++ b/test/support/smart-store.js @@ -3,11 +3,6 @@ var session = require('../../') var util = require('util') -/* istanbul ignore next */ -var defer = typeof setImmediate === 'function' - ? setImmediate - : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) } - module.exports = SmartStore function SmartStore () { @@ -19,7 +14,7 @@ util.inherits(SmartStore, session.Store) SmartStore.prototype.destroy = function destroy (sid, callback) { delete this.sessions[sid] - defer(callback, null) + setImmediate(callback, null) } SmartStore.prototype.get = function get (sid, callback) { @@ -45,10 +40,10 @@ SmartStore.prototype.get = function get (sid, callback) { } } - defer(callback, null, sess) + setImmediate(callback, null, sess) } SmartStore.prototype.set = function set (sid, sess, callback) { this.sessions[sid] = JSON.stringify(sess) - defer(callback, null) + setImmediate(callback, null) }