github.com/solo-io/unik@v0.0.0-20190717152701-a58d3e8e33b7/docs/examples/example-nodejs-fileserver/node_modules/express/lib/response.js (about)

     1  /*!
     2   * express
     3   * Copyright(c) 2009-2013 TJ Holowaychuk
     4   * Copyright(c) 2014-2015 Douglas Christopher Wilson
     5   * MIT Licensed
     6   */
     7  
     8  'use strict';
     9  
    10  /**
    11   * Module dependencies.
    12   * @private
    13   */
    14  
    15  var contentDisposition = require('content-disposition');
    16  var deprecate = require('depd')('express');
    17  var escapeHtml = require('escape-html');
    18  var http = require('http');
    19  var isAbsolute = require('./utils').isAbsolute;
    20  var onFinished = require('on-finished');
    21  var path = require('path');
    22  var merge = require('utils-merge');
    23  var sign = require('cookie-signature').sign;
    24  var normalizeType = require('./utils').normalizeType;
    25  var normalizeTypes = require('./utils').normalizeTypes;
    26  var setCharset = require('./utils').setCharset;
    27  var statusCodes = http.STATUS_CODES;
    28  var cookie = require('cookie');
    29  var send = require('send');
    30  var extname = path.extname;
    31  var mime = send.mime;
    32  var resolve = path.resolve;
    33  var vary = require('vary');
    34  
    35  /**
    36   * Response prototype.
    37   */
    38  
    39  var res = module.exports = {
    40    __proto__: http.ServerResponse.prototype
    41  };
    42  
    43  /**
    44   * Module variables.
    45   * @private
    46   */
    47  
    48  var charsetRegExp = /;\s*charset\s*=/;
    49  
    50  /**
    51   * Set status `code`.
    52   *
    53   * @param {Number} code
    54   * @return {ServerResponse}
    55   * @public
    56   */
    57  
    58  res.status = function status(code) {
    59    this.statusCode = code;
    60    return this;
    61  };
    62  
    63  /**
    64   * Set Link header field with the given `links`.
    65   *
    66   * Examples:
    67   *
    68   *    res.links({
    69   *      next: 'http://api.example.com/users?page=2',
    70   *      last: 'http://api.example.com/users?page=5'
    71   *    });
    72   *
    73   * @param {Object} links
    74   * @return {ServerResponse}
    75   * @public
    76   */
    77  
    78  res.links = function(links){
    79    var link = this.get('Link') || '';
    80    if (link) link += ', ';
    81    return this.set('Link', link + Object.keys(links).map(function(rel){
    82      return '<' + links[rel] + '>; rel="' + rel + '"';
    83    }).join(', '));
    84  };
    85  
    86  /**
    87   * Send a response.
    88   *
    89   * Examples:
    90   *
    91   *     res.send(new Buffer('wahoo'));
    92   *     res.send({ some: 'json' });
    93   *     res.send('<p>some html</p>');
    94   *
    95   * @param {string|number|boolean|object|Buffer} body
    96   * @public
    97   */
    98  
    99  res.send = function send(body) {
   100    var chunk = body;
   101    var encoding;
   102    var len;
   103    var req = this.req;
   104    var type;
   105  
   106    // settings
   107    var app = this.app;
   108  
   109    // allow status / body
   110    if (arguments.length === 2) {
   111      // res.send(body, status) backwards compat
   112      if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
   113        deprecate('res.send(body, status): Use res.status(status).send(body) instead');
   114        this.statusCode = arguments[1];
   115      } else {
   116        deprecate('res.send(status, body): Use res.status(status).send(body) instead');
   117        this.statusCode = arguments[0];
   118        chunk = arguments[1];
   119      }
   120    }
   121  
   122    // disambiguate res.send(status) and res.send(status, num)
   123    if (typeof chunk === 'number' && arguments.length === 1) {
   124      // res.send(status) will set status message as text string
   125      if (!this.get('Content-Type')) {
   126        this.type('txt');
   127      }
   128  
   129      deprecate('res.send(status): Use res.sendStatus(status) instead');
   130      this.statusCode = chunk;
   131      chunk = statusCodes[chunk];
   132    }
   133  
   134    switch (typeof chunk) {
   135      // string defaulting to html
   136      case 'string':
   137        if (!this.get('Content-Type')) {
   138          this.type('html');
   139        }
   140        break;
   141      case 'boolean':
   142      case 'number':
   143      case 'object':
   144        if (chunk === null) {
   145          chunk = '';
   146        } else if (Buffer.isBuffer(chunk)) {
   147          if (!this.get('Content-Type')) {
   148            this.type('bin');
   149          }
   150        } else {
   151          return this.json(chunk);
   152        }
   153        break;
   154    }
   155  
   156    // write strings in utf-8
   157    if (typeof chunk === 'string') {
   158      encoding = 'utf8';
   159      type = this.get('Content-Type');
   160  
   161      // reflect this in content-type
   162      if (typeof type === 'string') {
   163        this.set('Content-Type', setCharset(type, 'utf-8'));
   164      }
   165    }
   166  
   167    // populate Content-Length
   168    if (chunk !== undefined) {
   169      if (!Buffer.isBuffer(chunk)) {
   170        // convert chunk to Buffer; saves later double conversions
   171        chunk = new Buffer(chunk, encoding);
   172        encoding = undefined;
   173      }
   174  
   175      len = chunk.length;
   176      this.set('Content-Length', len);
   177    }
   178  
   179    // populate ETag
   180    var etag;
   181    var generateETag = len !== undefined && app.get('etag fn');
   182    if (typeof generateETag === 'function' && !this.get('ETag')) {
   183      if ((etag = generateETag(chunk, encoding))) {
   184        this.set('ETag', etag);
   185      }
   186    }
   187  
   188    // freshness
   189    if (req.fresh) this.statusCode = 304;
   190  
   191    // strip irrelevant headers
   192    if (204 == this.statusCode || 304 == this.statusCode) {
   193      this.removeHeader('Content-Type');
   194      this.removeHeader('Content-Length');
   195      this.removeHeader('Transfer-Encoding');
   196      chunk = '';
   197    }
   198  
   199    if (req.method === 'HEAD') {
   200      // skip body for HEAD
   201      this.end();
   202    } else {
   203      // respond
   204      this.end(chunk, encoding);
   205    }
   206  
   207    return this;
   208  };
   209  
   210  /**
   211   * Send JSON response.
   212   *
   213   * Examples:
   214   *
   215   *     res.json(null);
   216   *     res.json({ user: 'tj' });
   217   *
   218   * @param {string|number|boolean|object} obj
   219   * @public
   220   */
   221  
   222  res.json = function json(obj) {
   223    var val = obj;
   224  
   225    // allow status / body
   226    if (arguments.length === 2) {
   227      // res.json(body, status) backwards compat
   228      if (typeof arguments[1] === 'number') {
   229        deprecate('res.json(obj, status): Use res.status(status).json(obj) instead');
   230        this.statusCode = arguments[1];
   231      } else {
   232        deprecate('res.json(status, obj): Use res.status(status).json(obj) instead');
   233        this.statusCode = arguments[0];
   234        val = arguments[1];
   235      }
   236    }
   237  
   238    // settings
   239    var app = this.app;
   240    var replacer = app.get('json replacer');
   241    var spaces = app.get('json spaces');
   242    var body = JSON.stringify(val, replacer, spaces);
   243  
   244    // content-type
   245    if (!this.get('Content-Type')) {
   246      this.set('Content-Type', 'application/json');
   247    }
   248  
   249    return this.send(body);
   250  };
   251  
   252  /**
   253   * Send JSON response with JSONP callback support.
   254   *
   255   * Examples:
   256   *
   257   *     res.jsonp(null);
   258   *     res.jsonp({ user: 'tj' });
   259   *
   260   * @param {string|number|boolean|object} obj
   261   * @public
   262   */
   263  
   264  res.jsonp = function jsonp(obj) {
   265    var val = obj;
   266  
   267    // allow status / body
   268    if (arguments.length === 2) {
   269      // res.json(body, status) backwards compat
   270      if (typeof arguments[1] === 'number') {
   271        deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead');
   272        this.statusCode = arguments[1];
   273      } else {
   274        deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead');
   275        this.statusCode = arguments[0];
   276        val = arguments[1];
   277      }
   278    }
   279  
   280    // settings
   281    var app = this.app;
   282    var replacer = app.get('json replacer');
   283    var spaces = app.get('json spaces');
   284    var body = JSON.stringify(val, replacer, spaces);
   285    var callback = this.req.query[app.get('jsonp callback name')];
   286  
   287    // content-type
   288    if (!this.get('Content-Type')) {
   289      this.set('X-Content-Type-Options', 'nosniff');
   290      this.set('Content-Type', 'application/json');
   291    }
   292  
   293    // fixup callback
   294    if (Array.isArray(callback)) {
   295      callback = callback[0];
   296    }
   297  
   298    // jsonp
   299    if (typeof callback === 'string' && callback.length !== 0) {
   300      this.charset = 'utf-8';
   301      this.set('X-Content-Type-Options', 'nosniff');
   302      this.set('Content-Type', 'text/javascript');
   303  
   304      // restrict callback charset
   305      callback = callback.replace(/[^\[\]\w$.]/g, '');
   306  
   307      // replace chars not allowed in JavaScript that are in JSON
   308      body = body
   309        .replace(/\u2028/g, '\\u2028')
   310        .replace(/\u2029/g, '\\u2029');
   311  
   312      // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
   313      // the typeof check is just to reduce client error noise
   314      body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
   315    }
   316  
   317    return this.send(body);
   318  };
   319  
   320  /**
   321   * Send given HTTP status code.
   322   *
   323   * Sets the response status to `statusCode` and the body of the
   324   * response to the standard description from node's http.STATUS_CODES
   325   * or the statusCode number if no description.
   326   *
   327   * Examples:
   328   *
   329   *     res.sendStatus(200);
   330   *
   331   * @param {number} statusCode
   332   * @public
   333   */
   334  
   335  res.sendStatus = function sendStatus(statusCode) {
   336    var body = statusCodes[statusCode] || String(statusCode);
   337  
   338    this.statusCode = statusCode;
   339    this.type('txt');
   340  
   341    return this.send(body);
   342  };
   343  
   344  /**
   345   * Transfer the file at the given `path`.
   346   *
   347   * Automatically sets the _Content-Type_ response header field.
   348   * The callback `callback(err)` is invoked when the transfer is complete
   349   * or when an error occurs. Be sure to check `res.sentHeader`
   350   * if you wish to attempt responding, as the header and some data
   351   * may have already been transferred.
   352   *
   353   * Options:
   354   *
   355   *   - `maxAge`   defaulting to 0 (can be string converted by `ms`)
   356   *   - `root`     root directory for relative filenames
   357   *   - `headers`  object of headers to serve with file
   358   *   - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
   359   *
   360   * Other options are passed along to `send`.
   361   *
   362   * Examples:
   363   *
   364   *  The following example illustrates how `res.sendFile()` may
   365   *  be used as an alternative for the `static()` middleware for
   366   *  dynamic situations. The code backing `res.sendFile()` is actually
   367   *  the same code, so HTTP cache support etc is identical.
   368   *
   369   *     app.get('/user/:uid/photos/:file', function(req, res){
   370   *       var uid = req.params.uid
   371   *         , file = req.params.file;
   372   *
   373   *       req.user.mayViewFilesFrom(uid, function(yes){
   374   *         if (yes) {
   375   *           res.sendFile('/uploads/' + uid + '/' + file);
   376   *         } else {
   377   *           res.send(403, 'Sorry! you cant see that.');
   378   *         }
   379   *       });
   380   *     });
   381   *
   382   * @public
   383   */
   384  
   385  res.sendFile = function sendFile(path, options, callback) {
   386    var done = callback;
   387    var req = this.req;
   388    var res = this;
   389    var next = req.next;
   390    var opts = options || {};
   391  
   392    if (!path) {
   393      throw new TypeError('path argument is required to res.sendFile');
   394    }
   395  
   396    // support function as second arg
   397    if (typeof options === 'function') {
   398      done = options;
   399      opts = {};
   400    }
   401  
   402    if (!opts.root && !isAbsolute(path)) {
   403      throw new TypeError('path must be absolute or specify root to res.sendFile');
   404    }
   405  
   406    // create file stream
   407    var pathname = encodeURI(path);
   408    var file = send(req, pathname, opts);
   409  
   410    // transfer
   411    sendfile(res, file, opts, function (err) {
   412      if (done) return done(err);
   413      if (err && err.code === 'EISDIR') return next();
   414  
   415      // next() all but write errors
   416      if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
   417        next(err);
   418      }
   419    });
   420  };
   421  
   422  /**
   423   * Transfer the file at the given `path`.
   424   *
   425   * Automatically sets the _Content-Type_ response header field.
   426   * The callback `callback(err)` is invoked when the transfer is complete
   427   * or when an error occurs. Be sure to check `res.sentHeader`
   428   * if you wish to attempt responding, as the header and some data
   429   * may have already been transferred.
   430   *
   431   * Options:
   432   *
   433   *   - `maxAge`   defaulting to 0 (can be string converted by `ms`)
   434   *   - `root`     root directory for relative filenames
   435   *   - `headers`  object of headers to serve with file
   436   *   - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
   437   *
   438   * Other options are passed along to `send`.
   439   *
   440   * Examples:
   441   *
   442   *  The following example illustrates how `res.sendfile()` may
   443   *  be used as an alternative for the `static()` middleware for
   444   *  dynamic situations. The code backing `res.sendfile()` is actually
   445   *  the same code, so HTTP cache support etc is identical.
   446   *
   447   *     app.get('/user/:uid/photos/:file', function(req, res){
   448   *       var uid = req.params.uid
   449   *         , file = req.params.file;
   450   *
   451   *       req.user.mayViewFilesFrom(uid, function(yes){
   452   *         if (yes) {
   453   *           res.sendfile('/uploads/' + uid + '/' + file);
   454   *         } else {
   455   *           res.send(403, 'Sorry! you cant see that.');
   456   *         }
   457   *       });
   458   *     });
   459   *
   460   * @public
   461   */
   462  
   463  res.sendfile = function (path, options, callback) {
   464    var done = callback;
   465    var req = this.req;
   466    var res = this;
   467    var next = req.next;
   468    var opts = options || {};
   469  
   470    // support function as second arg
   471    if (typeof options === 'function') {
   472      done = options;
   473      opts = {};
   474    }
   475  
   476    // create file stream
   477    var file = send(req, path, opts);
   478  
   479    // transfer
   480    sendfile(res, file, opts, function (err) {
   481      if (done) return done(err);
   482      if (err && err.code === 'EISDIR') return next();
   483  
   484      // next() all but write errors
   485      if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') {
   486        next(err);
   487      }
   488    });
   489  };
   490  
   491  res.sendfile = deprecate.function(res.sendfile,
   492    'res.sendfile: Use res.sendFile instead');
   493  
   494  /**
   495   * Transfer the file at the given `path` as an attachment.
   496   *
   497   * Optionally providing an alternate attachment `filename`,
   498   * and optional callback `callback(err)`. The callback is invoked
   499   * when the data transfer is complete, or when an error has
   500   * ocurred. Be sure to check `res.headersSent` if you plan to respond.
   501   *
   502   * This method uses `res.sendfile()`.
   503   *
   504   * @public
   505   */
   506  
   507  res.download = function download(path, filename, callback) {
   508    var done = callback;
   509    var name = filename;
   510  
   511    // support function as second arg
   512    if (typeof filename === 'function') {
   513      done = filename;
   514      name = null;
   515    }
   516  
   517    // set Content-Disposition when file is sent
   518    var headers = {
   519      'Content-Disposition': contentDisposition(name || path)
   520    };
   521  
   522    // Resolve the full path for sendFile
   523    var fullPath = resolve(path);
   524  
   525    return this.sendFile(fullPath, { headers: headers }, done);
   526  };
   527  
   528  /**
   529   * Set _Content-Type_ response header with `type` through `mime.lookup()`
   530   * when it does not contain "/", or set the Content-Type to `type` otherwise.
   531   *
   532   * Examples:
   533   *
   534   *     res.type('.html');
   535   *     res.type('html');
   536   *     res.type('json');
   537   *     res.type('application/json');
   538   *     res.type('png');
   539   *
   540   * @param {String} type
   541   * @return {ServerResponse} for chaining
   542   * @public
   543   */
   544  
   545  res.contentType =
   546  res.type = function contentType(type) {
   547    var ct = type.indexOf('/') === -1
   548      ? mime.lookup(type)
   549      : type;
   550  
   551    return this.set('Content-Type', ct);
   552  };
   553  
   554  /**
   555   * Respond to the Acceptable formats using an `obj`
   556   * of mime-type callbacks.
   557   *
   558   * This method uses `req.accepted`, an array of
   559   * acceptable types ordered by their quality values.
   560   * When "Accept" is not present the _first_ callback
   561   * is invoked, otherwise the first match is used. When
   562   * no match is performed the server responds with
   563   * 406 "Not Acceptable".
   564   *
   565   * Content-Type is set for you, however if you choose
   566   * you may alter this within the callback using `res.type()`
   567   * or `res.set('Content-Type', ...)`.
   568   *
   569   *    res.format({
   570   *      'text/plain': function(){
   571   *        res.send('hey');
   572   *      },
   573   *
   574   *      'text/html': function(){
   575   *        res.send('<p>hey</p>');
   576   *      },
   577   *
   578   *      'appliation/json': function(){
   579   *        res.send({ message: 'hey' });
   580   *      }
   581   *    });
   582   *
   583   * In addition to canonicalized MIME types you may
   584   * also use extnames mapped to these types:
   585   *
   586   *    res.format({
   587   *      text: function(){
   588   *        res.send('hey');
   589   *      },
   590   *
   591   *      html: function(){
   592   *        res.send('<p>hey</p>');
   593   *      },
   594   *
   595   *      json: function(){
   596   *        res.send({ message: 'hey' });
   597   *      }
   598   *    });
   599   *
   600   * By default Express passes an `Error`
   601   * with a `.status` of 406 to `next(err)`
   602   * if a match is not made. If you provide
   603   * a `.default` callback it will be invoked
   604   * instead.
   605   *
   606   * @param {Object} obj
   607   * @return {ServerResponse} for chaining
   608   * @public
   609   */
   610  
   611  res.format = function(obj){
   612    var req = this.req;
   613    var next = req.next;
   614  
   615    var fn = obj.default;
   616    if (fn) delete obj.default;
   617    var keys = Object.keys(obj);
   618  
   619    var key = keys.length > 0
   620      ? req.accepts(keys)
   621      : false;
   622  
   623    this.vary("Accept");
   624  
   625    if (key) {
   626      this.set('Content-Type', normalizeType(key).value);
   627      obj[key](req, this, next);
   628    } else if (fn) {
   629      fn();
   630    } else {
   631      var err = new Error('Not Acceptable');
   632      err.status = err.statusCode = 406;
   633      err.types = normalizeTypes(keys).map(function(o){ return o.value });
   634      next(err);
   635    }
   636  
   637    return this;
   638  };
   639  
   640  /**
   641   * Set _Content-Disposition_ header to _attachment_ with optional `filename`.
   642   *
   643   * @param {String} filename
   644   * @return {ServerResponse}
   645   * @public
   646   */
   647  
   648  res.attachment = function attachment(filename) {
   649    if (filename) {
   650      this.type(extname(filename));
   651    }
   652  
   653    this.set('Content-Disposition', contentDisposition(filename));
   654  
   655    return this;
   656  };
   657  
   658  /**
   659   * Append additional header `field` with value `val`.
   660   *
   661   * Example:
   662   *
   663   *    res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
   664   *    res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
   665   *    res.append('Warning', '199 Miscellaneous warning');
   666   *
   667   * @param {String} field
   668   * @param {String|Array} val
   669   * @return {ServerResponse} for chaining
   670   * @public
   671   */
   672  
   673  res.append = function append(field, val) {
   674    var prev = this.get(field);
   675    var value = val;
   676  
   677    if (prev) {
   678      // concat the new and prev vals
   679      value = Array.isArray(prev) ? prev.concat(val)
   680        : Array.isArray(val) ? [prev].concat(val)
   681        : [prev, val];
   682    }
   683  
   684    return this.set(field, value);
   685  };
   686  
   687  /**
   688   * Set header `field` to `val`, or pass
   689   * an object of header fields.
   690   *
   691   * Examples:
   692   *
   693   *    res.set('Foo', ['bar', 'baz']);
   694   *    res.set('Accept', 'application/json');
   695   *    res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
   696   *
   697   * Aliased as `res.header()`.
   698   *
   699   * @param {String|Object} field
   700   * @param {String|Array} val
   701   * @return {ServerResponse} for chaining
   702   * @public
   703   */
   704  
   705  res.set =
   706  res.header = function header(field, val) {
   707    if (arguments.length === 2) {
   708      var value = Array.isArray(val)
   709        ? val.map(String)
   710        : String(val);
   711  
   712      // add charset to content-type
   713      if (field.toLowerCase() === 'content-type' && !charsetRegExp.test(value)) {
   714        var charset = mime.charsets.lookup(value.split(';')[0]);
   715        if (charset) value += '; charset=' + charset.toLowerCase();
   716      }
   717  
   718      this.setHeader(field, value);
   719    } else {
   720      for (var key in field) {
   721        this.set(key, field[key]);
   722      }
   723    }
   724    return this;
   725  };
   726  
   727  /**
   728   * Get value for header `field`.
   729   *
   730   * @param {String} field
   731   * @return {String}
   732   * @public
   733   */
   734  
   735  res.get = function(field){
   736    return this.getHeader(field);
   737  };
   738  
   739  /**
   740   * Clear cookie `name`.
   741   *
   742   * @param {String} name
   743   * @param {Object} options
   744   * @return {ServerResponse} for chaining
   745   * @public
   746   */
   747  
   748  res.clearCookie = function clearCookie(name, options) {
   749    var opts = merge({ expires: new Date(1), path: '/' }, options);
   750  
   751    return this.cookie(name, '', opts);
   752  };
   753  
   754  /**
   755   * Set cookie `name` to `value`, with the given `options`.
   756   *
   757   * Options:
   758   *
   759   *    - `maxAge`   max-age in milliseconds, converted to `expires`
   760   *    - `signed`   sign the cookie
   761   *    - `path`     defaults to "/"
   762   *
   763   * Examples:
   764   *
   765   *    // "Remember Me" for 15 minutes
   766   *    res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
   767   *
   768   *    // save as above
   769   *    res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
   770   *
   771   * @param {String} name
   772   * @param {String|Object} value
   773   * @param {Options} options
   774   * @return {ServerResponse} for chaining
   775   * @public
   776   */
   777  
   778  res.cookie = function (name, value, options) {
   779    var opts = merge({}, options);
   780    var secret = this.req.secret;
   781    var signed = opts.signed;
   782  
   783    if (signed && !secret) {
   784      throw new Error('cookieParser("secret") required for signed cookies');
   785    }
   786  
   787    var val = typeof value === 'object'
   788      ? 'j:' + JSON.stringify(value)
   789      : String(value);
   790  
   791    if (signed) {
   792      val = 's:' + sign(val, secret);
   793    }
   794  
   795    if ('maxAge' in opts) {
   796      opts.expires = new Date(Date.now() + opts.maxAge);
   797      opts.maxAge /= 1000;
   798    }
   799  
   800    if (opts.path == null) {
   801      opts.path = '/';
   802    }
   803  
   804    this.append('Set-Cookie', cookie.serialize(name, String(val), opts));
   805  
   806    return this;
   807  };
   808  
   809  /**
   810   * Set the location header to `url`.
   811   *
   812   * The given `url` can also be "back", which redirects
   813   * to the _Referrer_ or _Referer_ headers or "/".
   814   *
   815   * Examples:
   816   *
   817   *    res.location('/foo/bar').;
   818   *    res.location('http://example.com');
   819   *    res.location('../login');
   820   *
   821   * @param {String} url
   822   * @return {ServerResponse} for chaining
   823   * @public
   824   */
   825  
   826  res.location = function location(url) {
   827    var loc = url;
   828  
   829    // "back" is an alias for the referrer
   830    if (url === 'back') {
   831      loc = this.req.get('Referrer') || '/';
   832    }
   833  
   834    // set location
   835    this.set('Location', loc);
   836    return this;
   837  };
   838  
   839  /**
   840   * Redirect to the given `url` with optional response `status`
   841   * defaulting to 302.
   842   *
   843   * The resulting `url` is determined by `res.location()`, so
   844   * it will play nicely with mounted apps, relative paths,
   845   * `"back"` etc.
   846   *
   847   * Examples:
   848   *
   849   *    res.redirect('/foo/bar');
   850   *    res.redirect('http://example.com');
   851   *    res.redirect(301, 'http://example.com');
   852   *    res.redirect('../login'); // /blog/post/1 -> /blog/login
   853   *
   854   * @public
   855   */
   856  
   857  res.redirect = function redirect(url) {
   858    var address = url;
   859    var body;
   860    var status = 302;
   861  
   862    // allow status / url
   863    if (arguments.length === 2) {
   864      if (typeof arguments[0] === 'number') {
   865        status = arguments[0];
   866        address = arguments[1];
   867      } else {
   868        deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
   869        status = arguments[1];
   870      }
   871    }
   872  
   873    // Set location header
   874    this.location(address);
   875    address = this.get('Location');
   876  
   877    // Support text/{plain,html} by default
   878    this.format({
   879      text: function(){
   880        body = statusCodes[status] + '. Redirecting to ' + encodeURI(address);
   881      },
   882  
   883      html: function(){
   884        var u = escapeHtml(address);
   885        body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
   886      },
   887  
   888      default: function(){
   889        body = '';
   890      }
   891    });
   892  
   893    // Respond
   894    this.statusCode = status;
   895    this.set('Content-Length', Buffer.byteLength(body));
   896  
   897    if (this.req.method === 'HEAD') {
   898      this.end();
   899    } else {
   900      this.end(body);
   901    }
   902  };
   903  
   904  /**
   905   * Add `field` to Vary. If already present in the Vary set, then
   906   * this call is simply ignored.
   907   *
   908   * @param {Array|String} field
   909   * @return {ServerResponse} for chaining
   910   * @public
   911   */
   912  
   913  res.vary = function(field){
   914    // checks for back-compat
   915    if (!field || (Array.isArray(field) && !field.length)) {
   916      deprecate('res.vary(): Provide a field name');
   917      return this;
   918    }
   919  
   920    vary(this, field);
   921  
   922    return this;
   923  };
   924  
   925  /**
   926   * Render `view` with the given `options` and optional callback `fn`.
   927   * When a callback function is given a response will _not_ be made
   928   * automatically, otherwise a response of _200_ and _text/html_ is given.
   929   *
   930   * Options:
   931   *
   932   *  - `cache`     boolean hinting to the engine it should cache
   933   *  - `filename`  filename of the view being rendered
   934   *
   935   * @public
   936   */
   937  
   938  res.render = function render(view, options, callback) {
   939    var app = this.req.app;
   940    var done = callback;
   941    var opts = options || {};
   942    var req = this.req;
   943    var self = this;
   944  
   945    // support callback function as second arg
   946    if (typeof options === 'function') {
   947      done = options;
   948      opts = {};
   949    }
   950  
   951    // merge res.locals
   952    opts._locals = self.locals;
   953  
   954    // default callback to respond
   955    done = done || function (err, str) {
   956      if (err) return req.next(err);
   957      self.send(str);
   958    };
   959  
   960    // render
   961    app.render(view, opts, done);
   962  };
   963  
   964  // pipe the send file stream
   965  function sendfile(res, file, options, callback) {
   966    var done = false;
   967    var streaming;
   968  
   969    // request aborted
   970    function onaborted() {
   971      if (done) return;
   972      done = true;
   973  
   974      var err = new Error('Request aborted');
   975      err.code = 'ECONNABORTED';
   976      callback(err);
   977    }
   978  
   979    // directory
   980    function ondirectory() {
   981      if (done) return;
   982      done = true;
   983  
   984      var err = new Error('EISDIR, read');
   985      err.code = 'EISDIR';
   986      callback(err);
   987    }
   988  
   989    // errors
   990    function onerror(err) {
   991      if (done) return;
   992      done = true;
   993      callback(err);
   994    }
   995  
   996    // ended
   997    function onend() {
   998      if (done) return;
   999      done = true;
  1000      callback();
  1001    }
  1002  
  1003    // file
  1004    function onfile() {
  1005      streaming = false;
  1006    }
  1007  
  1008    // finished
  1009    function onfinish(err) {
  1010      if (err && err.code === 'ECONNRESET') return onaborted();
  1011      if (err) return onerror(err);
  1012      if (done) return;
  1013  
  1014      setImmediate(function () {
  1015        if (streaming !== false && !done) {
  1016          onaborted();
  1017          return;
  1018        }
  1019  
  1020        if (done) return;
  1021        done = true;
  1022        callback();
  1023      });
  1024    }
  1025  
  1026    // streaming
  1027    function onstream() {
  1028      streaming = true;
  1029    }
  1030  
  1031    file.on('directory', ondirectory);
  1032    file.on('end', onend);
  1033    file.on('error', onerror);
  1034    file.on('file', onfile);
  1035    file.on('stream', onstream);
  1036    onFinished(res, onfinish);
  1037  
  1038    if (options.headers) {
  1039      // set headers on successful transfer
  1040      file.on('headers', function headers(res) {
  1041        var obj = options.headers;
  1042        var keys = Object.keys(obj);
  1043  
  1044        for (var i = 0; i < keys.length; i++) {
  1045          var k = keys[i];
  1046          res.setHeader(k, obj[k]);
  1047        }
  1048      });
  1049    }
  1050  
  1051    // pipe
  1052    file.pipe(res);
  1053  }