express

4.17.14.18.2
lib/response.js
~lib/response.jsModified
+57−30
Index: package/lib/response.js
===================================================================
--- package/lib/response.js
+++ package/lib/response.js
@@ -13,8 +13,9 @@
  */
 
 var Buffer = require('safe-buffer').Buffer
 var contentDisposition = require('content-disposition');
+var createError = require('http-errors')
 var deprecate = require('depd')('express');
 var encodeUrl = require('encodeurl');
 var escapeHtml = require('escape-html');
 var http = require('http');
@@ -63,8 +64,11 @@
  * @public
  */
 
 res.status = function status(code) {
+  if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) {
+    deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead')
+  }
   this.statusCode = code;
   return this;
 };
 
@@ -134,9 +138,9 @@
     }
 
     deprecate('res.send(status): Use res.sendStatus(status) instead');
     this.statusCode = chunk;
-    chunk = statuses[chunk]
+    chunk = statuses.message[chunk]
   }
 
   switch (typeof chunk) {
     // string defaulting to html
@@ -212,8 +216,15 @@
     this.removeHeader('Transfer-Encoding');
     chunk = '';
   }
 
+  // alter headers for 205
+  if (this.statusCode === 205) {
+    this.set('Content-Length', '0')
+    this.removeHeader('Transfer-Encoding')
+    chunk = ''
+  }
+
   if (req.method === 'HEAD') {
     // skip body for HEAD
     this.end();
   } else {
@@ -283,11 +294,11 @@
   var val = obj;
 
   // allow status / body
   if (arguments.length === 2) {
-    // res.json(body, status) backwards compat
+    // res.jsonp(body, status) backwards compat
     if (typeof arguments[1] === 'number') {
-      deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead');
+      deprecate('res.jsonp(obj, status): Use res.status(status).jsonp(obj) instead');
       this.statusCode = arguments[1];
     } else {
       deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead');
       this.statusCode = arguments[0];
@@ -321,12 +332,17 @@
 
     // restrict callback charset
     callback = callback.replace(/[^\[\]\w$.]/g, '');
 
-    // replace chars not allowed in JavaScript that are in JSON
-    body = body
-      .replace(/\u2028/g, '\\u2028')
-      .replace(/\u2029/g, '\\u2029');
+    if (body === undefined) {
+      // empty argument
+      body = ''
+    } else if (typeof body === 'string') {
+      // replace chars not allowed in JavaScript that are in JSON
+      body = body
+        .replace(/\u2028/g, '\\u2028')
+        .replace(/\u2029/g, '\\u2029')
+    }
 
     // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
     // the typeof check is just to reduce client error noise
     body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
@@ -350,9 +366,9 @@
  * @public
  */
 
 res.sendStatus = function sendStatus(statusCode) {
-  var body = statuses[statusCode] || String(statusCode)
+  var body = statuses.message[statusCode] || String(statusCode)
 
   this.statusCode = statusCode;
   this.type('txt');
 
@@ -363,9 +379,9 @@
  * Transfer the file at the given `path`.
  *
  * Automatically sets the _Content-Type_ response header field.
  * The callback `callback(err)` is invoked when the transfer is complete
- * or when an error occurs. Be sure to check `res.sentHeader`
+ * or when an error occurs. Be sure to check `res.headersSent`
  * if you wish to attempt responding, as the header and some data
  * may have already been transferred.
  *
  * Options:
@@ -445,9 +461,9 @@
  * Transfer the file at the given `path`.
  *
  * Automatically sets the _Content-Type_ response header field.
  * The callback `callback(err)` is invoked when the transfer is complete
- * or when an error occurs. Be sure to check `res.sentHeader`
+ * or when an error occurs. Be sure to check `res.headersSent`
  * if you wish to attempt responding, as the header and some data
  * may have already been transferred.
  *
  * Options:
@@ -518,9 +534,9 @@
  *
  * Optionally providing an alternate attachment `filename`,
  * and optional callback `callback(err)`. The callback is invoked
  * when the data transfer is complete, or when an error has
- * ocurred. Be sure to check `res.headersSent` if you plan to respond.
+ * occurred. Be sure to check `res.headersSent` if you plan to respond.
  *
  * Optionally providing an `options` object to use with `res.sendFile()`.
  * This function will set the `Content-Disposition` header, overriding
  * any `Content-Disposition` header passed as header options in order
@@ -545,8 +561,15 @@
     done = options
     opts = null
   }
 
+  // support optional filename, where options may be in it's place
+  if (typeof filename === 'object' &&
+    (typeof options === 'function' || options === undefined)) {
+    name = null
+    opts = filename
+  }
+
   // set Content-Disposition when file is sent
   var headers = {
     'Content-Disposition': contentDisposition(name || path)
   };
@@ -566,9 +589,11 @@
   opts = Object.create(opts)
   opts.headers = headers
 
   // Resolve the full path for sendFile
-  var fullPath = resolve(path);
+  var fullPath = !opts.root
+    ? resolve(path)
+    : path
 
   // send file
   return this.sendFile(fullPath, opts, done)
 };
@@ -622,9 +647,9 @@
  *      'text/html': function(){
  *        res.send('<p>hey</p>');
  *      },
  *
- *      'appliation/json': function(){
+ *      'application/json': function () {
  *        res.send({ message: 'hey' });
  *      }
  *    });
  *
@@ -659,11 +684,10 @@
 res.format = function(obj){
   var req = this.req;
   var next = req.next;
 
-  var fn = obj.default;
-  if (fn) delete obj.default;
-  var keys = Object.keys(obj);
+  var keys = Object.keys(obj)
+    .filter(function (v) { return v !== 'default' })
 
   var key = keys.length > 0
     ? req.accepts(keys)
     : false;
@@ -672,15 +696,14 @@
 
   if (key) {
     this.set('Content-Type', normalizeType(key).value);
     obj[key](req, this, next);
-  } else if (fn) {
-    fn();
+  } else if (obj.default) {
+    obj.default(req, this, next)
   } else {
-    var err = new Error('Not Acceptable');
-    err.status = err.statusCode = 406;
-    err.types = normalizeTypes(keys).map(function(o){ return o.value });
-    next(err);
+    next(createError(406, {
+      types: normalizeTypes(keys).map(function (o) { return o.value })
+    }))
   }
 
   return this;
 };
@@ -725,9 +748,9 @@
   if (prev) {
     // concat the new and prev vals
     value = Array.isArray(prev) ? prev.concat(val)
       : Array.isArray(val) ? [prev].concat(val)
-      : [prev, val];
+        : [prev, val]
   }
 
   return this.set(field, value);
 };
@@ -844,11 +867,15 @@
   if (signed) {
     val = 's:' + sign(val, secret);
   }
 
-  if ('maxAge' in opts) {
-    opts.expires = new Date(Date.now() + opts.maxAge);
-    opts.maxAge /= 1000;
+  if (opts.maxAge != null) {
+    var maxAge = opts.maxAge - 0
+
+    if (!isNaN(maxAge)) {
+      opts.expires = new Date(Date.now() + maxAge)
+      opts.maxAge = Math.floor(maxAge / 1000)
+    }
   }
 
   if (opts.path == null) {
     opts.path = '/';
@@ -927,14 +954,14 @@
 
   // Support text/{plain,html} by default
   this.format({
     text: function(){
-      body = statuses[status] + '. Redirecting to ' + address
+      body = statuses.message[status] + '. Redirecting to ' + address
     },
 
     html: function(){
       var u = escapeHtml(address);
-      body = '<p>' + statuses[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
+      body = '<p>' + statuses.message[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
     },
 
     default: function(){
       body = '';
@@ -1107,9 +1134,9 @@
  * Stringify JSON, like JSON.stringify, but v8 optimized, with the
  * ability to escape characters that can trigger HTML sniffing.
  *
  * @param {*} value
- * @param {function} replaces
+ * @param {function} replacer
  * @param {number} spaces
  * @param {boolean} escape
  * @returns {string}
  * @private
@@ -1121,9 +1148,9 @@
   var json = replacer || spaces
     ? JSON.stringify(value, replacer, spaces)
     : JSON.stringify(value);
 
-  if (escape) {
+  if (escape && typeof json === 'string') {
     json = json.replace(/[<>&]/g, function (c) {
       switch (c.charCodeAt(0)) {
         case 0x3c:
           return '\\u003c'