示例#1
0
文件: index.js 项目: trevorah/session
  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
    var originalPath = parseUrl.original(req).pathname;
    if (0 != originalPath.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) next(new Error('`secret` option required for sessions'));

    var originalHash
      , originalId;

    // expose store
    req.sessionStore = store;

    // get the session ID from the cookie
    var cookieId = req.sessionID = getcookie(req, name, secret);

    // set-cookie
    onHeaders(res, function(){
      if (!req.session) {
        debug('no session');
        return;
      }

      var cookie = req.session.cookie;

      // only send secure cookies via https
      if (cookie.secure && !issecure(req, trustProxy)) {
        debug('not secured');
        return;
      }

      if (!shouldSetCookie(req)) {
        return;
      }

      setcookie(res, name, req.sessionID, secret, cookie.data);
    });

    // 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) {
        return false;
      }

      ended = true;

      var ret;
      var sync = true;

      if (chunk === undefined || chunk === null) {
        chunk = '';
      }

      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
        debug('destroying');
        store.destroy(req.sessionID, function ondestroy(err) {
          if (err) {
            defer(next, err);
          }

          debug('destroyed');
          writeend();
        });

        return writetop();
      }

      // no session to save
      if (!req.session) {
        debug('no session');
        return _end.call(res, chunk, encoding);
      }

      req.session.resetMaxAge();

      if (shouldSave(req)) {
        debug('saving');
        req.session.save(function onsave(err) {
          if (err) {
            defer(next, err);
          }

          debug('saved');
          writeend();
        });

        return writetop();
      }

      return _end.call(res, chunk, encoding);
    };

    // generate the session
    function generate() {
      store.generate(req);
      originalId = req.sessionID;
      originalHash = hash(req.session);
    }

    // check if session has been modified
    function isModified(sess) {
      return originalHash != hash(sess) || originalId != sess.id;
    }

    // determine if session should be destroyed
    function shouldDestroy(req) {
      return req.sessionID && unsetDestroy && req.session == null;
    }

    // determine if session should be saved to store
    function shouldSave(req) {
      return cookieId != req.sessionID
        ? saveUninitializedSession || isModified(req.session)
        : resaveSession || isModified(req.session);
    }

    // determine if cookie should be set on response
    function shouldSetCookie(req) {
      // in case of rolling session, always reset the cookie
      if (rollingSessions) {
        return true;
      }

      return cookieId != req.sessionID
        ? saveUninitializedSession || isModified(req.session)
        : req.session.cookie.expires != null && isModified(req.session);
    }

    // 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
    debug('fetching %s', req.sessionID);
    store.get(req.sessionID, function(err, sess){
      // 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();
      }
    });
  };
示例#2
0
文件: index.js 项目: 2eron/session
  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
    var originalPath = parse(req.originalUrl).pathname;
    if (0 != originalPath.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');

    var originalHash
      , originalId;

    // expose store
    req.sessionStore = store;

    // grab the session cookie value and check the signature
    var rawCookie = req.cookies[name];

    // get signedCookies for backwards compat with signed cookies
    var unsignedCookie = req.signedCookies[name];

    if (!unsignedCookie && rawCookie) {
      unsignedCookie = (0 == rawCookie.indexOf('s:'))
        ? signature.unsign(rawCookie.slice(2), secret)
        : rawCookie;
    }

    // set-cookie
    onHeaders(res, function(){
      if (!req.session) {
        debug('no 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) {
        debug('not secured');
        return;
      }

      // in case of rolling session, always reset the cookie
      if (!rollingSessions) {

        // browser-session length cookie
        if (null == cookie.expires) {
          if (!isNew) {
            debug('already set browser-session cookie');
            return
          }
        // compare hashes and ids
        } else if (originalHash == hash(req.session) && originalId == req.session.id) {
          debug('unmodified session');
          return
        }

      }

      var val = 's:' + signature.sign(req.sessionID, secret);
      debug('set-cookie %s', val);
      res.cookie(name, val, cookie.data);
    });

    // 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
    debug('fetching %s', req.sessionID);
    store.get(req.sessionID, function(err, sess){
      // 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();
      }
    });
  };
示例#3
0
  return function cookieSession(req, res, next){
    var cookies = req.sessionCookies = new Cookies(req, res, keys);
    var sess, json;

    // to pass to Session()
    req.sessionOptions = {};
    for (var key in opts) {
      req.sessionOptions[key] = opts[key];
    };
    req.sessionKey = name;

    req.__defineGetter__('session', function(){
      // already retrieved
      if (sess) return sess;

      // unset
      if (false === sess) return null;

      json = cookies.get(name, opts);

      if (json) {
        debug('parse %s', json);
        try {
          sess = new Session(req, decode(json));
        } catch (err) {
          // backwards compatibility:
          // create a new session if parsing fails.
          // new Buffer(string, 'base64') does not seem to crash
          // when `string` is not base64-encoded.
          // but `JSON.parse(string)` will crash.
          if (!(err instanceof SyntaxError)) throw err;
          sess = new Session(req);
        }
      } else {
        debug('new session');
        sess = new Session(req);
      }

      return sess;
    });

    req.__defineSetter__('session', function(val){
      if (null == val) return sess = false;
      if ('object' == typeof val) return sess = new Session(req, val);
      throw new Error('req.session can only be set as null or an object.');
    });

    onHeaders(res, function setHeaders() {
      if (sess === undefined) {
        // not accessed
        return;
      }

      try {
        if (sess === false) {
          // remove
          cookies.set(name, '', opts);
        } else if (!json && !sess.length) {
          // do nothing if new and not populated
        } else if (sess.changed(json)) {
          // save
          sess.save();
        }
      } catch (e) {
        debug('error saving session %s', e.message);
      }
    });

    next();
  }
示例#4
0
  return function compression(req, res, next){
    var ended = false
    var length
    var listeners = []
    var write = res.write
    var on = res.on
    var end = res.end
    var stream

    // flush
    res.flush = function flush() {
      if (stream) {
        stream.flush()
      }
    }

    // proxy

    res.write = function(chunk, encoding){
      if (ended) {
        return false
      }

      if (!this._header) {
        this._implicitHeader()
      }

      return stream
        ? stream.write(new Buffer(chunk, encoding))
        : write.call(this, chunk, encoding)
    };

    res.end = function(chunk, encoding){
      if (ended) {
        return false
      }

      if (!this._header) {
        // estimate the length
        if (!this.getHeader('Content-Length')) {
          length = chunkLength(chunk, encoding)
        }

        this._implicitHeader()
      }

      if (!stream) {
        return end.call(this, chunk, encoding)
      }

      // mark ended
      ended = true

      // write Buffer for Node.js 0.8
      return chunk
        ? stream.end(new Buffer(chunk, encoding))
        : stream.end()
    };

    res.on = function(type, listener){
      if (!listeners || type !== 'drain') {
        return on.call(this, type, listener)
      }

      if (stream) {
        return stream.on(type, listener)
      }

      // buffer listeners for future stream
      listeners.push([type, listener])

      return this
    }

    function nocompress(msg) {
      debug('no compression: %s', msg)
      addListeners(res, on, listeners)
      listeners = null
    }

    onHeaders(res, function(){
      // determine if request is filtered
      if (!filter(req, res)) {
        nocompress('filtered')
        return
      }

      // determine if the entity should be transformed
      if (!shouldTransform(req, res)) {
        nocompress('no transform')
        return
      }

      // vary
      vary(res, 'Accept-Encoding')

      // content-length below threshold
      if (Number(res.getHeader('Content-Length')) < threshold || length < threshold) {
        nocompress('size below threshold')
        return
      }

      var encoding = res.getHeader('Content-Encoding') || 'identity';

      // already encoded
      if ('identity' !== encoding) {
        nocompress('already encoded')
        return
      }

      // head
      if ('HEAD' === req.method) {
        nocompress('HEAD request')
        return
      }

      var contentType = res.getHeader('Content-Type');

      // compression method
      var accept = accepts(req)
      // send in each compression method separately to ignore client preference and
      // instead enforce server preference. also, server-sent events (mime type of
      // text/event-stream) require flush functionality, so skip brotli in that
      // case.
      var method = (contentType !== "text/event-stream" && accept.encoding('br'))
        || accept.encoding('gzip')
        || accept.encoding('deflate')
        || accept.encoding('identity');

      // negotiation failed
      if (!method || method === 'identity') {
        nocompress('not acceptable')
        return
      }

      // do we have this coding/url/etag combo in the cache?
      var etag = res.getHeader('ETag') || null;
      var cacheable = cache && shouldCache(req, res) && etag && res.statusCode >= 200 && res.statusCode < 300
      if (cacheable) {
        var buffer = cache.lookup(method, req.url, etag)
        if (buffer) {
          // the rest of the code expects a duplex stream, so
          // make a duplex stream that just ignores its input
          stream = new BufferDuplex(buffer)
        }
      }

      // if stream is not assigned, we got a cache miss and need to compress
      // the result
      if (!stream) {
        // compression stream
        debug('%s compression', method)
        switch (method) {
          case 'br':
            stream = iltorb.compressStream(brotliOpts)
            // brotli has no flush method. add a dummy flush method here.
            stream.flush = dummyBrotliFlush;
            break
          case 'gzip':
            stream = zlib.createGzip(zlibOpts)
            break
          case 'deflate':
            stream = zlib.createDeflate(zlibOpts)
            break
        }

        // if it is cacheable, let's keep hold of the compressed chunks and cache
        // them once the compression stream ends.
        if (cacheable) {
          var chunks = [];
          stream.on('data', function (chunk){
            chunks.push(chunk)
          })
          stream.on('end', function () {
            cache.add(method, req.url, etag, chunks)
          })
        }
      }

      // add buffered listeners to stream
      addListeners(stream, stream.on, listeners)

      // header fields
      res.setHeader('Content-Encoding', method);
      res.removeHeader('Content-Length');

      // compression
      stream.on('data', function(chunk){
        if (write.call(res, chunk) === false) {
          stream.pause()
        }
      });

      stream.on('end', function(){
        end.call(res);
      });

      on.call(res, 'drain', function() {
        stream.resume()
      });
    });

    next();
  };
示例#5
0
    log.request = function(req, res, next){
        for(let i = 0; i < opts.req_filter.length; i++){
            if(req.originalUrl.indexOf(opts.req_filter[i]) > -1)
                return next();
        }

        onHeaders(res, log_start.bind(res));
        onFinished(res, req_log);

        next();

        // =========

        function log_start(){
            this._log_start = process.hrtime();
        }
        function req_log(){
            const sc = res.statusCode < 400 ? 'green' : 'red';
            const ms = nano_time(res._log_start);

            const user = req._log_user ? chalk.blue(req._log_user) : chalk.grey(req.ip);
            let ref = req.get('referrer') || req.get('referer');
            if(ref){
                ref = ref.replace('http://', '');
                ref = ref.replace('https://', '');
            }


            // Args
            const args = [chalk.cyan('REQUEST'),'|'];

            if(opts.date)
                args.push(log.date(),'|');

            args.push(user);

            if(ref)
                args.push(chalk.grey(`from ${ref}`));

            args.push('|');

            args.push(req.method, chalk[sc](res.statusCode), ':', `${ms} ms`, '|',
                chalk.magenta(req.originalUrl)
            );

            console.log.apply(log, args);

            // ===

            function nano_time(start){
                let t = conv(process.hrtime()) - conv(start);   // ns
                t = Math.round(t / 1000);                       // µs
                return t / 1000;                                // ms [3 dec]

                // ====

                function conv(t){
                    if(!t | typeof t[0] === 'undefined')
                        return 0;

                    return t[0] * 1e9 + t[1];
                }
            }
        }
    };
示例#6
0
  return function compression(req, res, next){
    var compress = true
    var listeners = []
    var write = res.write
    var on = res.on
    var end = res.end
    var stream

    // see #8
    req.on('close', function(){
      res.write = res.end = function(){};
    });

    // flush is noop by default
    res.flush = noop;

    // proxy

    res.write = function(chunk, encoding){
      if (!this._header) {
        // if content-length is set and is lower
        // than the threshold, don't compress
        var len = Number(res.getHeader('Content-Length'))
        checkthreshold(len)
        this._implicitHeader();
      }
      return stream
        ? stream.write(new Buffer(chunk, encoding))
        : write.call(res, chunk, encoding);
    };

    res.end = function(chunk, encoding){
      var len

      if (chunk) {
        len = Buffer.isBuffer(chunk)
          ? chunk.length
          : Buffer.byteLength(chunk, encoding)
      }

      if (!this._header) {
        checkthreshold(len)
      }

      if (chunk) {
        this.write(chunk, encoding);
      }

      return stream
        ? stream.end()
        : end.call(res);
    };

    res.on = function(type, listener){
      if (!listeners || type !== 'drain') {
        return on.call(this, type, listener)
      }

      if (stream) {
        return stream.on(type, listener)
      }

      // buffer listeners for future stream
      listeners.push([type, listener])

      return this
    }

    function checkthreshold(len) {
      if (compress && len < threshold) {
        debug('size below threshold')
        compress = false
      }
    }

    function nocompress(msg) {
      debug('no compression' + (msg ? ': ' + msg : ''))
      addListeners(res, on, listeners)
      listeners = null
    }

    onHeaders(res, function(){
      // determine if request is filtered
      if (!filter(req, res)) {
        nocompress('filtered')
        return
      }

      // vary
      vary(res, 'Accept-Encoding')

      if (!compress) {
        nocompress()
        return
      }

      var encoding = res.getHeader('Content-Encoding') || 'identity';

      // already encoded
      if ('identity' !== encoding) {
        nocompress('already encoded')
        return
      }

      // head
      if ('HEAD' === req.method) {
        nocompress('HEAD request')
        return
      }

      // compression method
      var accept = accepts(req);
      var method = accept.encodings(['gzip', 'deflate', 'identity']);

      // negotiation failed
      if (!method || method === 'identity') {
        nocompress('not acceptable')
        return
      }

      // compression stream
      debug('%s compression', method)
      stream = exports.methods[method](options);
      addListeners(stream, stream.on, listeners)

      // overwrite the flush method
      res.flush = function(){
        stream.flush();
      }

      // header fields
      res.setHeader('Content-Encoding', method);
      res.removeHeader('Content-Length');

      // compression
      stream.on('data', function(chunk){
        if (write.call(res, chunk) === false) {
          stream.pause()
        }
      });

      stream.on('end', function(){
        end.call(res);
      });

      on.call(res, 'drain', function() {
        stream.resume()
      });
    });

    next();
  };
示例#7
0
  return function cookieSession(req, res, next) {

    // req.secret is for backwards compatibility
    var secret = options.secret || req.secret;
    if (!secret) throw new Error('`secret` option required for cookie sessions');

    // default session
    req.session = {};
    var cookie = req.session.cookie = new Cookie(options.cookie);

    // pathname mismatch
    var originalPath = parseUrl.original(req).pathname;
    if (0 != originalPath.indexOf(cookie.path)) return next();

    // cookieParser secret
    if (!options.secret && req.secret) {
      req.session = req.signedCookies[key] || {};
      req.session.cookie = cookie;
    } else {
      // TODO: refactor
      var rawCookie = req.cookies[key];
      if (rawCookie) {
        var unsigned = cookieParser.signedCookie(rawCookie, secret);
        if (unsigned) {
          var original = unsigned;
          req.session = cookieParser.JSONCookie(unsigned) || {};
          req.session.cookie = cookie;
        }
      }
    }

    onHeaders(res, function(){
      // removed
      if (!req.session) {
        debug('clear session');
        cookie.expires = new Date(0);
        res.setHeader('Set-Cookie', cookie.serialize(key, ''));
        return;
      }

      delete req.session.cookie;

      // check security
      var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase()
        , tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0]);

      // only send secure cookies via https
      if (cookie.secure && !tls) return debug('not secured');

      // serialize
      debug('serializing %j', req.session);
      var val = 'j:' + JSON.stringify(req.session);

      // compare data, no need to set-cookie if unchanged
      if (original == val) return debug('unmodified session');

      // set-cookie
      val = 's:' + signature.sign(val, secret);
      val = cookie.serialize(key, val);
      debug('set-cookie %j', cookie);
      res.setHeader('Set-Cookie', val);
    });

    next();
  };
示例#8
0
文件: update.js 项目: tropy/tropy.org
function noCache(res) {
  onHeaders(res, function () { this.removeHeader('ETag') })
  return res
}
示例#9
0
文件: index.js 项目: slmkrnz/Tester
  return function compression(req, res, next) {

    var ended = false;
    var length;
    var listeners = [];
    var write = res.write;
    var on = res.on;
    var end = res.end;
    var stream;

    // flush
    res.flush = function flush() {

      if (stream) {
        stream.flush();
      }
    };

    // proxy
    res.write = function(chunk, encoding) {

      if (ended === true) {
        return false;
      }
      if (!this._header) {
        this._implicitHeader();
      }
      return stream ? stream.write(new Buffer(chunk, encoding)) : write.call(
        res, chunk, encoding);
    };

    res.end = function(chunk, encoding) {

      if (ended === true) {
        return false;
      }
      if (!this._header) {
        // estimate the length
        if (!this.getHeader('Content-Length')) {
          length = chunkLength(chunk, encoding);
        }

        this._implicitHeader();
      }

      if (!stream) {
        return end.call(this, chunk, encoding);
      }

      // mark ended
      ended = true;

      // write Buffer for Node.js 0.8
      return chunk ? stream.end(new Buffer(chunk, encoding)) : stream.end();
    };

    res.on = function(type, listener) {

      if (!listeners || type !== 'drain') {
        return on.call(this, type, listener);
      }

      if (stream) {
        return stream.on(type, listener);
      }

      // buffer listeners for future stream
      listeners.push([ type, listener ]);
      return this;
    };

    function nocompress(msg) {

      debug('no compression: %s', msg);
      addListeners(res, on, listeners);
      listeners = null;
      return;
    }

    onHeaders(res, function() {

      // determine if request is filtered
      if (!filter(req, res)) {
        return nocompress('filtered');
      }

      // determine if the entity should be transformed
      if (!shouldTransform(req, res)) {
        nocompress('no transform');
        return;
      }

      // vary
      vary(res, 'Accept-Encoding');

      // content-length below threshold
      if (Number(res.getHeader('Content-Length')) < threshold
        || length < threshold) {
        nocompress('size below threshold');
        return;
      }

      var encoding = res.getHeader('Content-Encoding') || 'identity';

      if ('identity' !== encoding) { // already encoded
        return nocompress('already encoded');
      } else if ('HEAD' === req.method) { // head
        return nocompress('HEAD request');
      }

      // compression method
      var accept = accepts(req);
      var method = accept.encoding(available);

      // negotiation failed
      if (!method || method === 'identity') {
        return nocompress('not acceptable');
      }

      // compression stream
      if (method === 'gzip') {
        stream = zlib.createGzip(zlib_options);
      } else if (method === 'deflate') {
        stream = zlib.createDeflate(zlib_options);
      } else {
        return nocompress('not acceptable');
      }
      debug('%s compression', method);

      // add bufferred listeners to stream
      addListeners(stream, stream.on, listeners);

      // header fields
      res.setHeader('Content-Encoding', method);
      res.removeHeader('Content-Length');

      // compression
      stream.on('data', function(chunk) {

        if (write.call(res, chunk) === false) {
          stream.pause();
        }
        return;
      }).on('end', function() {

        return end.call(res);
      });
      on.call(res, 'drain', function() {

        return stream.resume();
      });
    });

    return next();
  };
  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
    var originalPath = parseUrl.original(req).pathname;
    if (0 != originalPath.indexOf(cookie.path || '/')) return next();

    // 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;

    // expose store
    req.sessionStore = store;

    // get the session ID from the cookie
    var cookieId = req.sessionID = getcookie(req, name, secrets);

    // set-cookie
    onHeaders(res, function(){
      if (!req.session) {
        debug('no session');
        return;
      }

      var cookie = req.session.cookie;

      // only send secure cookies via https
      if (cookie.secure && !issecure(req, trustProxy)) {
        debug('not secured');
        return;
      }

      if (!shouldSetCookie(req)) {
        return;
      }

      setcookie(res, name, req.sessionID, secrets[0], cookie.data);
    });

    // 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) {
        return false;
      }

      ended = true;

      var ret;
      var sync = true;

      function writeend() {
        if (sync) {
          ret = _end.call(res, chunk, encoding);
          sync = false;
          return;
        }

        _end.call(res);
      }

      function writetop() {
        if (!sync) {
          return ret;
        }

        if (chunk == null) {
          ret = true;
          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
        debug('destroying');
        store.destroy(req.sessionID, function ondestroy(err) {
          if (err) {
            defer(next, err);
          }

          debug('destroyed');
          writeend();
        });

        return writetop();
      }

      // no session to save
      if (!req.session) {
        debug('no session');
        return _end.call(res, chunk, encoding);
      }

      // touch session
      req.session.touch();

      if (shouldSave(req)) {
        req.session.save(function onsave(err) {
          if (err) {
            defer(next, err);
          }

          writeend();
        });

        return writetop();
      } else if (storeImplementsTouch && shouldTouch(req)) {
        // store implements touch method
        debug('touching');
        store.touch(req.sessionID, req.session, function ontouch(err) {
          if (err) {
            defer(next, err);
          }

          debug('touched');
          writeend();
        });

        return writetop();
      }

      return _end.call(res, chunk, encoding);
    };

    // generate the session
    function generate() {
      store.generate(req);
      originalId = req.sessionID;
      originalHash = hash(req.session);
      wrapmethods(req.session);
    }

    // wrap session methods
    function wrapmethods(sess) {
      var _save = sess.save;

      function save() {
        debug('saving %s', this.id);
        savedHash = hash(this);
        _save.apply(this, arguments);
      }

      Object.defineProperty(sess, 'save', {
        configurable: true,
        enumerable: false,
        value: save,
        writable: true
      });
    }

    // check if session has been modified
    function isModified(sess) {
      return originalId !== sess.id || originalHash !== hash(sess);
    }

    // check if session has been saved
    function isSaved(sess) {
      return originalId === sess.id && savedHash === hash(sess);
    }

    // determine if session should be destroyed
    function shouldDestroy(req) {
      return req.sessionID && unsetDestroy && req.session == null;
    }

    // determine if session should be saved to store
    function shouldSave(req) {
      // cannot set cookie without a session ID
      if (typeof req.sessionID !== 'string') {
        debug('session ignored because of bogus req.sessionID %o', req.sessionID);
        return false;
      }

      return !saveUninitializedSession && cookieId !== req.sessionID
        ? isModified(req.session)
        : !isSaved(req.session)
    }

    // determine if session should be touched
    function shouldTouch(req) {
      // cannot set cookie without a session ID
      if (typeof req.sessionID !== 'string') {
        debug('session ignored because of bogus req.sessionID %o', req.sessionID);
        return false;
      }

      return cookieId === req.sessionID && !shouldSave(req);
    }

    // determine if cookie should be set on response
    function shouldSetCookie(req) {
      // cannot set cookie without a session ID
      if (typeof req.sessionID !== 'string') {
        return false;
      }

      // in case of rolling session, always reset the cookie
      if (rollingSessions) {
        return true;
      }

      return cookieId != req.sessionID
        ? saveUninitializedSession || isModified(req.session)
        : req.session.cookie.expires != null && isModified(req.session);
    }

    // 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
    debug('fetching %s', req.sessionID);
    store.get(req.sessionID, function(err, sess){
      // error handling
      if (err) {
        debug('error %j', err);

        if (err.code !== 'ENOENT') {
          next(err);
          return;
        }

        generate();
      // no session
      } else if (!sess) {
        debug('no session found');
        generate();
      // populate req.session
      } else {
        debug('session found');
        store.createSession(req, sess);
        originalId = req.sessionID;
        originalHash = hash(sess);

        if (!resaveSession) {
          savedHash = originalHash
        }

        wrapmethods(req.session);
      }

      next();
    });
  };
示例#11
0
文件: index.js 项目: WebProFe/Toledo
  return function compression (req, res, next) {
    var ended = false
    var length
    var listeners = []
    var stream

    var _end = res.end
    var _on = res.on
    var _write = res.write

    // flush
    res.flush = function flush () {
      if (stream) {
        stream.flush()
      }
    }

    // proxy

    res.write = function write (chunk, encoding) {
      if (ended) {
        return false
      }

      if (!this._header) {
        this._implicitHeader()
      }

      return stream
        ? stream.write(toBuffer(chunk, encoding))
        : _write.call(this, chunk, encoding)
    }

    res.end = function end (chunk, encoding) {
      if (ended) {
        return false
      }

      if (!this._header) {
        // estimate the length
        if (!this.getHeader('Content-Length')) {
          length = chunkLength(chunk, encoding)
        }

        this._implicitHeader()
      }

      if (!stream) {
        return _end.call(this, chunk, encoding)
      }

      // mark ended
      ended = true

      // write Buffer for Node.js 0.8
      return chunk
        ? stream.end(toBuffer(chunk, encoding))
        : stream.end()
    }

    res.on = function on (type, listener) {
      if (!listeners || type !== 'drain') {
        return _on.call(this, type, listener)
      }

      if (stream) {
        return stream.on(type, listener)
      }

      // buffer listeners for future stream
      listeners.push([type, listener])

      return this
    }

    function nocompress (msg) {
      debug('no compression: %s', msg)
      addListeners(res, _on, listeners)
      listeners = null
    }

    onHeaders(res, function onResponseHeaders () {
      // determine if request is filtered
      if (!filter(req, res)) {
        nocompress('filtered')
        return
      }

      // determine if the entity should be transformed
      if (!shouldTransform(req, res)) {
        nocompress('no transform')
        return
      }

      // vary
      vary(res, 'Accept-Encoding')

      // content-length below threshold
      if (Number(res.getHeader('Content-Length')) < threshold || length < threshold) {
        nocompress('size below threshold')
        return
      }

      var encoding = res.getHeader('Content-Encoding') || 'identity'

      // already encoded
      if (encoding !== 'identity') {
        nocompress('already encoded')
        return
      }

      // head
      if (req.method === 'HEAD') {
        nocompress('HEAD request')
        return
      }

      // compression method
      var accept = accepts(req)
      var method = accept.encoding(['gzip', 'deflate', 'identity'])

      // we really don't prefer deflate
      if (method === 'deflate' && accept.encoding(['gzip'])) {
        method = accept.encoding(['gzip', 'identity'])
      }

      // negotiation failed
      if (!method || method === 'identity') {
        nocompress('not acceptable')
        return
      }

      // compression stream
      debug('%s compression', method)
      stream = method === 'gzip'
        ? zlib.createGzip(opts)
        : zlib.createDeflate(opts)

      // add buffered listeners to stream
      addListeners(stream, stream.on, listeners)

      // header fields
      res.setHeader('Content-Encoding', method)
      res.removeHeader('Content-Length')

      // compression
      stream.on('data', function onStreamData (chunk) {
        if (_write.call(res, chunk) === false) {
          stream.pause()
        }
      })

      stream.on('end', function onStreamEnd () {
        _end.call(res)
      })

      _on.call(res, 'drain', function onResponseDrain () {
        stream.resume()
      })
    })

    next()
  }
示例#12
0
 return function(req, res, next) {
   onHeaders(res, function onHeaders() {
     res.removeHeader(toStrip);
   });
   next();
 };