dox-foundation-demo node_modules/express/node_modules/connect/lib/middleware/session.js

Session

declaration
Session

    Description

    Module dependencies.

    Source

    var Session = require('./session/session')
      , debug = require('debug')('connect:session')
      , MemoryStore = require('./session/memory')
      , signature = require('cookie-signature')
      , Cookie = require('./session/cookie')
      , Store = require('./session/store')
      , utils = require('./../utils')
      , uid = require('uid2')
      , parse = utils.parseUrl
      , crc32 = require('buffer-crc32');
    
    // environment
    
    var env = process.env.NODE_ENV;

    Store

    property
    exports.Store

      Description

      Expose constructors.

      Source

      exports.Store = Store;
      exports.Cookie = Cookie;
      exports.Session = Session;
      exports.MemoryStore = MemoryStore;

      warning

      declaration
      warning

        Description

        Warning message for MemoryStore usage in production.

        Source

        var warning = 'Warning: connection.session() MemoryStore is not\n'
          + 'designed for a production environment, as it will leak\n'
          + 'memory, and will not scale past a single process.';

        session

        function
        session()
        • @param: {Object}options
        • @return: {Function}

        Description

        Session:

        Setup session store with the given options.

        Session data is not saved in the cookie itself, however
        cookies are used, so we must use the cookieParser()
        middleware before session().

        Examples:

        connect()
          .use(connect.cookieParser())
          .use(connect.session({ secret: 'keyboard cat', key: 'sid', cookie: { secure: true }}))
        

        Options:

        • key cookie name defaulting to connect.sid
        • store session store instance
        • secret session cookie is signed with this secret to prevent tampering
        • cookie session cookie settings, defaulting to { path: '/', httpOnly: true, maxAge: null }
        • proxy trust the reverse proxy when setting secure cookies (via "x-forwarded-proto")

        Cookie option:

        By default cookie.maxAge is null, meaning no "expires" parameter is set
        so the cookie becomes a browser-session cookie. When the user closes the
        browser the cookie (and session) will be removed.

        req.session

        To store or access session data, simply use the request property req.session,
        which is (generally) serialized as JSON by the store, so nested objects
        are typically fine. For example below is a user-specific view counter:

          connect()
            .use(connect.favicon())
            .use(connect.cookieParser())
            .use(connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))
            .use(function(req, res, next){
              var sess = req.session;
              if (sess.views) {
                res.setHeader('Content-Type', 'text/html');
                res.write('<p>views: ' + sess.views + '</p>');
                res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>');
                res.end();
                sess.views++;
              } else {
                sess.views = 1;
                res.end('welcome to the session demo. refresh!');
              }
            }
          )).listen(3000);
        

        Session#regenerate()

        To regenerate the session simply invoke the method, once complete
        a new SID and Session instance will be initialized at req.session.

         req.session.regenerate(function(err){
           // will have a new session here
         });
        

        Session#destroy()

        Destroys the session, removing req.session, will be re-generated next request.

         req.session.destroy(function(err){
           // cannot access session here
         });
        

        Session#reload()

        Reloads the session data.

         req.session.reload(function(err){
           // session updated
         });
        

        Session#save()

        Save the session.

         req.session.save(function(err){
           // session saved
         });
        

        Session#touch()

        Updates the .maxAge property. Typically this is
        not necessary to call, as the session middleware does this for you.

        Session#cookie

        Each session has a unique cookie object accompany it. This allows
        you to alter the session cookie per visitor. For example we can
        set req.session.cookie.expires to false to enable the cookie
        to remain for only the duration of the user-agent.

        Session#maxAge

        Alternatively req.session.cookie.maxAge will return the time
        remaining in milliseconds, which we may also re-assign a new value
        to adjust the .expires property appropriately. The following
        are essentially equivalent

        var hour = 3600000;
        req.session.cookie.expires = new Date(Date.now() + hour);
        req.session.cookie.maxAge = hour;
        

        For example when maxAge is set to 60000 (one minute), and 30 seconds
        has elapsed it will return 30000 until the current request has completed,
        at which time req.session.touch() is called to reset req.session.maxAge
        to its original value.

        req.session.cookie.maxAge;
        // => 30000
        

        Session Store Implementation:

        Every session store must implement the following methods

        • .get(sid, callback)
        • .set(sid, session, callback)
        • .destroy(sid, callback)

        Recommended methods include, but are not limited to:

        • .length(callback)
        • .clear(callback)

        For an example implementation view the connect-redis repo.

        Source

        function session(options){
          var options = options || {}
            , key = options.key || 'connect.sid'
            , store = options.store || new MemoryStore
            , cookie = options.cookie || {}
            , trustProxy = options.proxy
            , storeReady = true;
        
          // notify user that this store is not
          // meant for a production environment
          if ('production' == env && store instanceof MemoryStore) {
            console.warn(warning);
          }
        
          // generates the new session
          store.generate = function(req){
            req.sessionID = uid(24);
            req.session = new Session(req);
            req.session.cookie = new Cookie(cookie);
          };
        
          store.on('disconnect', function(){ storeReady = false; });
          store.on('connect', function(){ storeReady = true; });
        
          return function session(req, res, next) {
            // self-awareness
            if (req.session) return next();
        
            // Handle connection as if there is no session if
            // the store has temporarily disconnected etc
            if (!storeReady) return debug('store is disconnected'), next();
        
            // pathname mismatch
            if (0 != req.originalUrl.indexOf(cookie.path || '/')) return next();
        
            // backwards compatibility for signed cookies
            // req.secret is passed from the cookie parser middleware
            var secret = options.secret || req.secret;
        
            // ensure secret is available or bail
            if (!secret) throw new Error('`secret` option required for sessions');
        
            // parse url
            var originalHash
              , originalId;
        
            // expose store
            req.sessionStore = store;
        
            // grab the session cookie value and check the signature
            var rawCookie = req.cookies[key];
        
            // get signedCookies for backwards compat with signed cookies
            var unsignedCookie = req.signedCookies[key];
        
            if (!unsignedCookie && rawCookie) {
              unsignedCookie = utils.parseSignedCookie(rawCookie, secret);
            }
        
            // set-cookie
            res.on('header', function(){
              if (!req.session) return;
              var cookie = req.session.cookie
                , proto = (req.headers['x-forwarded-proto'] || '').split(',')[0].toLowerCase().trim()
                , tls = req.connection.encrypted || (trustProxy && 'https' == proto)
                , isNew = unsignedCookie != req.sessionID;
        
              // only send secure cookies via https
              if (cookie.secure && !tls) return debug('not secured');
        
              // long expires, handle expiry server-side
              if (!isNew && cookie.hasLongExpires) return debug('already set cookie');
        
              // browser-session length cookie
              if (null == cookie.expires) {
                if (!isNew) return debug('already set browser-session cookie');
              // compare hashes and ids
              } else if (originalHash == hash(req.session) && originalId == req.session.id) {
                return debug('unmodified session');
              }
        
              var val = 's:' + signature.sign(req.sessionID, secret);
              val = cookie.serialize(key, val);
              debug('set-cookie %s', val);
              res.setHeader('Set-Cookie', val);
            });
        
            // proxy end() to commit the session
            var end = res.end;
            res.end = function(data, encoding){
              res.end = end;
              if (!req.session) return res.end(data, encoding);
              debug('saving');
              req.session.resetMaxAge();
              req.session.save(function(err){
                if (err) console.error(err.stack);
                debug('saved');
                res.end(data, encoding);
              });
            };
        
            // generate the session
            function generate() {
              store.generate(req);
            }
        
            // get the sessionID from the cookie
            req.sessionID = unsignedCookie;
        
            // generate a session if the browser doesn't send a sessionID
            if (!req.sessionID) {
              debug('no SID sent, generating session');
              generate();
              next();
              return;
            }
        
            // generate the session object
            var pause = utils.pause(req);
            debug('fetching %s', req.sessionID);
            store.get(req.sessionID, function(err, sess){
              // proxy to resume() events
              var _next = next;
              next = function(err){
                _next(err);
                pause.resume();
              };
        
              // error handling
              if (err) {
                debug('error %j', err);
                if ('ENOENT' == err.code) {
                  generate();
                  next();
                } else {
                  next(err);
                }
              // no session
              } else if (!sess) {
                debug('no session found');
                generate();
                next();
              // populate req.session
              } else {
                debug('session found');
                store.createSession(req, sess);
                originalId = req.sessionID;
                originalHash = hash(sess);
                next();
              }
            });
          };
        };