lib/response.js JAVASCRIPT 1,048 lines View on github.com → Search inside
1/*!2 * express3 * Copyright(c) 2009-2013 TJ Holowaychuk4 * Copyright(c) 2014-2015 Douglas Christopher Wilson5 * MIT Licensed6 */78'use strict';910/**11 * Module dependencies.12 * @private13 */1415var contentDisposition = require('content-disposition');16var createError = require('http-errors')17var deprecate = require('depd')('express');18var encodeUrl = require('encodeurl');19var escapeHtml = require('escape-html');20var http = require('node:http');21var onFinished = require('on-finished');22var mime = require('mime-types')23var path = require('node:path');24var pathIsAbsolute = require('node:path').isAbsolute;25var statuses = require('statuses')26var sign = require('cookie-signature').sign;27var normalizeType = require('./utils').normalizeType;28var normalizeTypes = require('./utils').normalizeTypes;29var setCharset = require('./utils').setCharset;30var cookie = require('cookie');31var send = require('send');32var extname = path.extname;33var resolve = path.resolve;34var vary = require('vary');35const { Buffer } = require('node:buffer');3637/**38 * Response prototype.39 * @public40 */4142var res = Object.create(http.ServerResponse.prototype)4344/**45 * Module exports.46 * @public47 */4849module.exports = res5051/**52 * Set the HTTP status code for the response.53 *54 * Expects an integer value between 100 and 999 inclusive.55 * Throws an error if the provided status code is not an integer or if it's outside the allowable range.56 *57 * @param {number} code - The HTTP status code to set.58 * @return {ServerResponse} - Returns itself for chaining methods.59 * @throws {TypeError} If `code` is not an integer.60 * @throws {RangeError} If `code` is outside the range 100 to 999.61 * @public62 */6364res.status = function status(code) {65  // Check if the status code is not an integer66  if (!Number.isInteger(code)) {67    throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`);68  }69  // Check if the status code is outside of Node's valid range70  if (code < 100 || code > 999) {71    throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`);72  }7374  this.statusCode = code;75  return this;76};7778/**79 * Set Link header field with the given `links`.80 *81 * Examples:82 *83 *    res.links({84 *      next: 'http://api.example.com/users?page=2',85 *      last: 'http://api.example.com/users?page=5',86 *      pages: [87 *        'http://api.example.com/users?page=1',88 *        'http://api.example.com/users?page=2'89 *      ]90 *    });91 *92 * @param {Object} links93 * @return {ServerResponse}94 * @public95 */9697res.links = function(links) {98  var link = this.get('Link') || '';99  if (link) link += ', ';100  return this.set('Link', link + Object.keys(links).map(function(rel) {101    // Allow multiple links if links[rel] is an array102    if (Array.isArray(links[rel])) {103      return links[rel].map(function (singleLink) {104        return `<${singleLink}>; rel="${rel}"`;105      }).join(', ');106    } else {107      return `<${links[rel]}>; rel="${rel}"`;108    }109  }).join(', '));110};111112/**113 * Send a response.114 *115 * Examples:116 *117 *     res.send(Buffer.from('wahoo'));118 *     res.send({ some: 'json' });119 *     res.send('<p>some html</p>');120 *121 * @param {string|number|boolean|object|Buffer} body122 * @public123 */124125res.send = function send(body) {126  var chunk = body;127  var encoding;128  var req = this.req;129130  // settings131  var app = this.app;132133  switch (typeof chunk) {134    // string defaulting to html135    case 'string':136      encoding = 'utf8';137      const type = this.get('Content-Type');138139      if (typeof type === 'string') {140        this.set('Content-Type', setCharset(type, 'utf-8'));141      } else {142        this.type('html');143      }144      break;145    case 'boolean':146    case 'number':147    case 'object':148      if (chunk === null) {149        chunk = '';150      } else if (ArrayBuffer.isView(chunk)) {151        if (!this.get('Content-Type')) {152          this.type('bin');153        }154      } else {155        return this.json(chunk);156      }157      break;158  }159160  // determine if ETag should be generated161  var etagFn = app.get('etag fn')162  var generateETag = !this.get('ETag') && typeof etagFn === 'function'163164  // populate Content-Length165  var len166  if (chunk !== undefined) {167    if (Buffer.isBuffer(chunk)) {168      // get length of Buffer169      len = chunk.length170    } else if (!generateETag && chunk.length < 1000) {171      // just calculate length when no ETag + small chunk172      len = Buffer.byteLength(chunk, encoding)173    } else {174      // convert chunk to Buffer and calculate175      chunk = Buffer.from(chunk, encoding)176      encoding = undefined;177      len = chunk.length178    }179180    this.set('Content-Length', len);181  }182183  // populate ETag184  var etag;185  if (generateETag && len !== undefined) {186    if ((etag = etagFn(chunk, encoding))) {187      this.set('ETag', etag);188    }189  }190191  // freshness192  if (req.fresh) this.status(304);193194  // strip irrelevant headers195  if (204 === this.statusCode || 304 === this.statusCode) {196    this.removeHeader('Content-Type');197    this.removeHeader('Content-Length');198    this.removeHeader('Transfer-Encoding');199    chunk = '';200  }201202  // alter headers for 205203  if (this.statusCode === 205) {204    this.set('Content-Length', '0')205    this.removeHeader('Transfer-Encoding')206    chunk = ''207  }208209  if (req.method === 'HEAD') {210    // skip body for HEAD211    this.end();212  } else {213    // respond214    this.end(chunk, encoding);215  }216217  return this;218};219220/**221 * Send JSON response.222 *223 * Examples:224 *225 *     res.json(null);226 *     res.json({ user: 'tj' });227 *228 * @param {string|number|boolean|object} obj229 * @public230 */231232res.json = function json(obj) {233  // settings234  var app = this.app;235  var escape = app.get('json escape')236  var replacer = app.get('json replacer');237  var spaces = app.get('json spaces');238  var body = stringify(obj, replacer, spaces, escape)239240  // content-type241  if (!this.get('Content-Type')) {242    this.set('Content-Type', 'application/json');243  }244245  return this.send(body);246};247248/**249 * Send JSON response with JSONP callback support.250 *251 * Examples:252 *253 *     res.jsonp(null);254 *     res.jsonp({ user: 'tj' });255 *256 * @param {string|number|boolean|object} obj257 * @public258 */259260res.jsonp = function jsonp(obj) {261  // settings262  var app = this.app;263  var escape = app.get('json escape')264  var replacer = app.get('json replacer');265  var spaces = app.get('json spaces');266  var body = stringify(obj, replacer, spaces, escape)267  var callback = this.req.query[app.get('jsonp callback name')];268269  // content-type270  if (!this.get('Content-Type')) {271    this.set('X-Content-Type-Options', 'nosniff');272    this.set('Content-Type', 'application/json');273  }274275  // fixup callback276  if (Array.isArray(callback)) {277    callback = callback[0];278  }279280  // jsonp281  if (typeof callback === 'string' && callback.length !== 0) {282    this.set('X-Content-Type-Options', 'nosniff');283    this.set('Content-Type', 'text/javascript');284285    // restrict callback charset286    callback = callback.replace(/[^\[\]\w$.]/g, '');287288    if (body === undefined) {289      // empty argument290      body = ''291    } else if (typeof body === 'string') {292      // replace chars not allowed in JavaScript that are in JSON293      body = body294        .replace(/\u2028/g, '\\u2028')295        .replace(/\u2029/g, '\\u2029')296    }297298    // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"299    // the typeof check is just to reduce client error noise300    body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';301  }302303  return this.send(body);304};305306/**307 * Send given HTTP status code.308 *309 * Sets the response status to `statusCode` and the body of the310 * response to the standard description from node's http.STATUS_CODES311 * or the statusCode number if no description.312 *313 * Examples:314 *315 *     res.sendStatus(200);316 *317 * @param {number} statusCode318 * @public319 */320321res.sendStatus = function sendStatus(statusCode) {322  var body = statuses.message[statusCode] || String(statusCode)323324  this.status(statusCode);325  this.type('txt');326327  return this.send(body);328};329330/**331 * Transfer the file at the given `path`.332 *333 * Automatically sets the _Content-Type_ response header field.334 * The callback `callback(err)` is invoked when the transfer is complete335 * or when an error occurs. Be sure to check `res.headersSent`336 * if you wish to attempt responding, as the header and some data337 * may have already been transferred.338 *339 * Options:340 *341 *   - `maxAge`   defaulting to 0 (can be string converted by `ms`)342 *   - `root`     root directory for relative filenames343 *   - `headers`  object of headers to serve with file344 *   - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them345 *346 * Other options are passed along to `send`.347 *348 * Examples:349 *350 *  The following example illustrates how `res.sendFile()` may351 *  be used as an alternative for the `static()` middleware for352 *  dynamic situations. The code backing `res.sendFile()` is actually353 *  the same code, so HTTP cache support etc is identical.354 *355 *     app.get('/user/:uid/photos/:file', function(req, res){356 *       var uid = req.params.uid357 *         , file = req.params.file;358 *359 *       req.user.mayViewFilesFrom(uid, function(yes){360 *         if (yes) {361 *           res.sendFile('/uploads/' + uid + '/' + file);362 *         } else {363 *           res.send(403, 'Sorry! you cant see that.');364 *         }365 *       });366 *     });367 *368 * @public369 */370371res.sendFile = function sendFile(path, options, callback) {372  var done = callback;373  var req = this.req;374  var res = this;375  var next = req.next;376  var opts = options || {};377378  if (!path) {379    throw new TypeError('path argument is required to res.sendFile');380  }381382  if (typeof path !== 'string') {383    throw new TypeError('path must be a string to res.sendFile')384  }385386  // support function as second arg387  if (typeof options === 'function') {388    done = options;389    opts = {};390  }391392  if (!opts.root && !pathIsAbsolute(path)) {393    throw new TypeError('path must be absolute or specify root to res.sendFile');394  }395396  // create file stream397  var pathname = encodeURI(path);398399  // wire application etag option to send400  opts.etag = this.app.enabled('etag');401  var file = send(req, pathname, opts);402403  // transfer404  sendfile(res, file, opts, function (err) {405    if (done) return done(err);406    if (err && err.code === 'EISDIR') return next();407408    // next() all but write errors409    if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {410      next(err);411    }412  });413};414415/**416 * Transfer the file at the given `path` as an attachment.417 *418 * Optionally providing an alternate attachment `filename`,419 * and optional callback `callback(err)`. The callback is invoked420 * when the data transfer is complete, or when an error has421 * occurred. Be sure to check `res.headersSent` if you plan to respond.422 *423 * Optionally providing an `options` object to use with `res.sendFile()`.424 * This function will set the `Content-Disposition` header, overriding425 * any `Content-Disposition` header passed as header options in order426 * to set the attachment and filename.427 *428 * This method uses `res.sendFile()`.429 *430 * @public431 */432433res.download = function download (path, filename, options, callback) {434  var done = callback;435  var name = filename;436  var opts = options || null437438  // support function as second or third arg439  if (typeof filename === 'function') {440    done = filename;441    name = null;442    opts = null443  } else if (typeof options === 'function') {444    done = options445    opts = null446  }447448  // support optional filename, where options may be in it's place449  if (typeof filename === 'object' &&450    (typeof options === 'function' || options === undefined)) {451    name = null452    opts = filename453  }454455  // set Content-Disposition when file is sent456  var headers = {457    'Content-Disposition': contentDisposition(name || path)458  };459460  // merge user-provided headers461  if (opts && opts.headers) {462    var keys = Object.keys(opts.headers)463    for (var i = 0; i < keys.length; i++) {464      var key = keys[i]465      if (key.toLowerCase() !== 'content-disposition') {466        headers[key] = opts.headers[key]467      }468    }469  }470471  // merge user-provided options472  opts = Object.create(opts)473  opts.headers = headers474475  // Resolve the full path for sendFile476  var fullPath = !opts.root477    ? resolve(path)478    : path479480  // send file481  return this.sendFile(fullPath, opts, done)482};483484/**485 * Set _Content-Type_ response header with `type` through `mime.contentType()`486 * when it does not contain "/", or set the Content-Type to `type` otherwise.487 * When no mapping is found though `mime.contentType()`, the type is set to488 * "application/octet-stream".489 *490 * Examples:491 *492 *     res.type('.html');493 *     res.type('html');494 *     res.type('json');495 *     res.type('application/json');496 *     res.type('png');497 *498 * @param {String} type499 * @return {ServerResponse} for chaining500 * @public501 */502503res.contentType =504res.type = function contentType(type) {505  var ct = type.indexOf('/') === -1506    ? (mime.contentType(type) || 'application/octet-stream')507    : type;508509  return this.set('Content-Type', ct);510};511512/**513 * Respond to the Acceptable formats using an `obj`514 * of mime-type callbacks.515 *516 * This method uses `req.accepted`, an array of517 * acceptable types ordered by their quality values.518 * When "Accept" is not present the _first_ callback519 * is invoked, otherwise the first match is used. When520 * no match is performed the server responds with521 * 406 "Not Acceptable".522 *523 * Content-Type is set for you, however if you choose524 * you may alter this within the callback using `res.type()`525 * or `res.set('Content-Type', ...)`.526 *527 *    res.format({528 *      'text/plain': function(){529 *        res.send('hey');530 *      },531 *532 *      'text/html': function(){533 *        res.send('<p>hey</p>');534 *      },535 *536 *      'application/json': function () {537 *        res.send({ message: 'hey' });538 *      }539 *    });540 *541 * In addition to canonicalized MIME types you may542 * also use extnames mapped to these types:543 *544 *    res.format({545 *      text: function(){546 *        res.send('hey');547 *      },548 *549 *      html: function(){550 *        res.send('<p>hey</p>');551 *      },552 *553 *      json: function(){554 *        res.send({ message: 'hey' });555 *      }556 *    });557 *558 * By default Express passes an `Error`559 * with a `.status` of 406 to `next(err)`560 * if a match is not made. If you provide561 * a `.default` callback it will be invoked562 * instead.563 *564 * @param {Object} obj565 * @return {ServerResponse} for chaining566 * @public567 */568569res.format = function(obj){570  var req = this.req;571  var next = req.next;572573  var keys = Object.keys(obj)574    .filter(function (v) { return v !== 'default' })575576  var key = keys.length > 0577    ? req.accepts(keys)578    : false;579580  this.vary("Accept");581582  if (key) {583    this.set('Content-Type', normalizeType(key).value);584    obj[key](req, this, next);585  } else if (obj.default) {586    obj.default(req, this, next)587  } else {588    next(createError(406, {589      types: normalizeTypes(keys).map(function (o) { return o.value })590    }))591  }592593  return this;594};595596/**597 * Set _Content-Disposition_ header to _attachment_ with optional `filename`.598 *599 * @param {String} filename600 * @return {ServerResponse}601 * @public602 */603604res.attachment = function attachment(filename) {605  if (filename) {606    this.type(extname(filename));607  }608609  this.set('Content-Disposition', contentDisposition(filename));610611  return this;612};613614/**615 * Append additional header `field` with value `val`.616 *617 * Example:618 *619 *    res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);620 *    res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');621 *    res.append('Warning', '199 Miscellaneous warning');622 *623 * @param {String} field624 * @param {String|Array} val625 * @return {ServerResponse} for chaining626 * @public627 */628629res.append = function append(field, val) {630  var prev = this.get(field);631  var value = val;632633  if (prev) {634    // concat the new and prev vals635    value = Array.isArray(prev) ? prev.concat(val)636      : Array.isArray(val) ? [prev].concat(val)637        : [prev, val]638  }639640  return this.set(field, value);641};642643/**644 * Set header `field` to `val`, or pass645 * an object of header fields.646 *647 * Examples:648 *649 *    res.set('Foo', ['bar', 'baz']);650 *    res.set('Accept', 'application/json');651 *    res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });652 *653 * Aliased as `res.header()`.654 *655 * When the set header is "Content-Type", the type is expanded to include656 * the charset if not present using `mime.contentType()`.657 *658 * @param {String|Object} field659 * @param {String|Array} val660 * @return {ServerResponse} for chaining661 * @public662 */663664res.set =665res.header = function header(field, val) {666  if (arguments.length === 2) {667    var value = Array.isArray(val)668      ? val.map(String)669      : String(val);670671    // add charset to content-type672    if (field.toLowerCase() === 'content-type') {673      if (Array.isArray(value)) {674        throw new TypeError('Content-Type cannot be set to an Array');675      }676      value = mime.contentType(value)677    }678679    this.setHeader(field, value);680  } else {681    for (var key in field) {682      this.set(key, field[key]);683    }684  }685  return this;686};687688/**689 * Get value for header `field`.690 *691 * @param {String} field692 * @return {String}693 * @public694 */695696res.get = function(field){697  return this.getHeader(field);698};699700/**701 * Clear cookie `name`.702 *703 * @param {String} name704 * @param {Object} [options]705 * @return {ServerResponse} for chaining706 * @public707 */708709res.clearCookie = function clearCookie(name, options) {710  // Force cookie expiration by setting expires to the past711  const opts = { path: '/', ...options, expires: new Date(1)};712  // ensure maxAge is not passed713  delete opts.maxAge714715  return this.cookie(name, '', opts);716};717718/**719 * Set cookie `name` to `value`, with the given `options`.720 *721 * Options:722 *723 *    - `maxAge`   max-age in milliseconds, converted to `expires`724 *    - `signed`   sign the cookie725 *    - `path`     defaults to "/"726 *727 * Examples:728 *729 *    // "Remember Me" for 15 minutes730 *    res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });731 *732 *    // same as above733 *    res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })734 *735 * @param {String} name736 * @param {String|Object} value737 * @param {Object} [options]738 * @return {ServerResponse} for chaining739 * @public740 */741742res.cookie = function (name, value, options) {743  var opts = { ...options };744  var secret = this.req.secret;745  var signed = opts.signed;746747  if (signed && !secret) {748    throw new Error('cookieParser("secret") required for signed cookies');749  }750751  var val = typeof value === 'object'752    ? 'j:' + JSON.stringify(value)753    : String(value);754755  if (signed) {756    val = 's:' + sign(val, secret);757  }758759  if (opts.maxAge != null) {760    var maxAge = opts.maxAge - 0761762    if (!isNaN(maxAge)) {763      opts.expires = new Date(Date.now() + maxAge)764      opts.maxAge = Math.floor(maxAge / 1000)765    }766  }767768  if (opts.path == null) {769    opts.path = '/';770  }771772  this.append('Set-Cookie', cookie.serialize(name, String(val), opts));773774  return this;775};776777/**778 * Set the location header to `url`.779 *780 * The given `url` can also be "back", which redirects781 * to the _Referrer_ or _Referer_ headers or "/".782 *783 * Examples:784 *785 *    res.location('/foo/bar').;786 *    res.location('http://example.com');787 *    res.location('../login');788 *789 * @param {String} url790 * @return {ServerResponse} for chaining791 * @public792 */793794res.location = function location(url) {795  return this.set('Location', encodeUrl(url));796};797798/**799 * Redirect to the given `url` with optional response `status`800 * defaulting to 302.801 *802 * Examples:803 *804 *    res.redirect('/foo/bar');805 *    res.redirect('http://example.com');806 *    res.redirect(301, 'http://example.com');807 *    res.redirect('../login'); // /blog/post/1 -> /blog/login808 *809 * @public810 */811812res.redirect = function redirect(url) {813  var address = url;814  var body;815  var status = 302;816817  // allow status / url818  if (arguments.length === 2) {819    status = arguments[0]820    address = arguments[1]821  }822823  if (!address) {824    deprecate('Provide a url argument');825  }826827  if (typeof address !== 'string') {828    deprecate('Url must be a string');829  }830831  if (typeof status !== 'number') {832    deprecate('Status must be a number');833  }834835  // Set location header836  address = this.location(address).get('Location');837838  // Support text/{plain,html} by default839  this.format({840    text: function(){841      body = statuses.message[status] + '. Redirecting to ' + address842    },843844    html: function(){845      var u = escapeHtml(address);846      body = '<!DOCTYPE html><head><title>' + statuses.message[status] + '</title></head>'847       + '<body><p>' + statuses.message[status] + '. Redirecting to ' + u + '</p></body>'848    },849850    default: function(){851      body = '';852    }853  });854855  // Respond856  this.status(status);857  this.set('Content-Length', Buffer.byteLength(body));858859  if (this.req.method === 'HEAD') {860    this.end();861  } else {862    this.end(body);863  }864};865866/**867 * Add `field` to Vary. If already present in the Vary set, then868 * this call is simply ignored.869 *870 * @param {Array|String} field871 * @return {ServerResponse} for chaining872 * @public873 */874875res.vary = function(field){876  vary(this, field);877878  return this;879};880881/**882 * Render `view` with the given `options` and optional callback `fn`.883 * When a callback function is given a response will _not_ be made884 * automatically, otherwise a response of _200_ and _text/html_ is given.885 *886 * Options:887 *888 *  - `cache`     boolean hinting to the engine it should cache889 *  - `filename`  filename of the view being rendered890 *891 * @public892 */893894res.render = function render(view, options, callback) {895  var app = this.req.app;896  var done = callback;897  var opts = options || {};898  var req = this.req;899  var self = this;900901  // support callback function as second arg902  if (typeof options === 'function') {903    done = options;904    opts = {};905  }906907  // merge res.locals908  opts._locals = self.locals;909910  // default callback to respond911  done = done || function (err, str) {912    if (err) return req.next(err);913    self.send(str);914  };915916  // render917  app.render(view, opts, done);918};919920// pipe the send file stream921function sendfile(res, file, options, callback) {922  var done = false;923  var streaming;924925  // request aborted926  function onaborted() {927    if (done) return;928    done = true;929930    var err = new Error('Request aborted');931    err.code = 'ECONNABORTED';932    callback(err);933  }934935  // directory936  function ondirectory() {937    if (done) return;938    done = true;939940    var err = new Error('EISDIR, read');941    err.code = 'EISDIR';942    callback(err);943  }944945  // errors946  function onerror(err) {947    if (done) return;948    done = true;949    callback(err);950  }951952  // ended953  function onend() {954    if (done) return;955    done = true;956    callback();957  }958959  // file960  function onfile() {961    streaming = false;962  }963964  // finished965  function onfinish(err) {966    if (err && err.code === 'ECONNRESET') return onaborted();967    if (err) return onerror(err);968    if (done) return;969970    setImmediate(function () {971      if (streaming !== false && !done) {972        onaborted();973        return;974      }975976      if (done) return;977      done = true;978      callback();979    });980  }981982  // streaming983  function onstream() {984    streaming = true;985  }986987  file.on('directory', ondirectory);988  file.on('end', onend);989  file.on('error', onerror);990  file.on('file', onfile);991  file.on('stream', onstream);992  onFinished(res, onfinish);993994  if (options.headers) {995    // set headers on successful transfer996    file.on('headers', function headers(res) {997      var obj = options.headers;998      var keys = Object.keys(obj);9991000      for (var i = 0; i < keys.length; i++) {1001        var k = keys[i];1002        res.setHeader(k, obj[k]);1003      }1004    });1005  }10061007  // pipe1008  file.pipe(res);1009}10101011/**1012 * Stringify JSON, like JSON.stringify, but v8 optimized, with the1013 * ability to escape characters that can trigger HTML sniffing.1014 *1015 * @param {*} value1016 * @param {function} replacer1017 * @param {number} spaces1018 * @param {boolean} escape1019 * @returns {string}1020 * @private1021 */10221023function stringify (value, replacer, spaces, escape) {1024  // v8 checks arguments.length for optimizing simple call1025  // https://bugs.chromium.org/p/v8/issues/detail?id=47301026  var json = replacer || spaces1027    ? JSON.stringify(value, replacer, spaces)1028    : JSON.stringify(value);10291030  if (escape && typeof json === 'string') {1031    json = json.replace(/[<>&]/g, function (c) {1032      switch (c.charCodeAt(0)) {1033        case 0x3c:1034          return '\\u003c'1035        case 0x3e:1036          return '\\u003e'1037        case 0x26:1038          return '\\u0026'1039        /* istanbul ignore next: unreachable default */1040        default:1041          return c1042      }1043    })1044  }10451046  return json1047}

Code quality findings 100

Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var contentDisposition = require('content-disposition');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var createError = require('http-errors')
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var deprecate = require('depd')('express');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var encodeUrl = require('encodeurl');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var escapeHtml = require('escape-html');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var http = require('node:http');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var onFinished = require('on-finished');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var mime = require('mime-types')
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var path = require('node:path');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var pathIsAbsolute = require('node:path').isAbsolute;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var statuses = require('statuses')
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var sign = require('cookie-signature').sign;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var normalizeType = require('./utils').normalizeType;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var normalizeTypes = require('./utils').normalizeTypes;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var setCharset = require('./utils').setCharset;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var cookie = require('cookie');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var send = require('send');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var extname = path.extname;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var resolve = path.resolve;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var vary = require('vary');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var res = Object.create(http.ServerResponse.prototype)
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var link = this.get('Link') || '';
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var chunk = body;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var encoding;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var req = this.req;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var app = this.app;
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
switch (typeof chunk) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (typeof chunk) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof type === 'string') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof type === 'string') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (chunk === null) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var etagFn = app.get('etag fn')
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var generateETag = !this.get('ETag') && typeof etagFn === 'function'
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
var generateETag = !this.get('ETag') && typeof etagFn === 'function'
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
var generateETag = !this.get('ETag') && typeof etagFn === 'function'
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var len
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (chunk !== undefined) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var etag;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (generateETag && len !== undefined) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (204 === this.statusCode || 304 === this.statusCode) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (this.statusCode === 205) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (req.method === 'HEAD') {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var app = this.app;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var escape = app.get('json escape')
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var replacer = app.get('json replacer');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var spaces = app.get('json spaces');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var body = stringify(obj, replacer, spaces, escape)
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var app = this.app;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var escape = app.get('json escape')
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var replacer = app.get('json replacer');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var spaces = app.get('json spaces');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var body = stringify(obj, replacer, spaces, escape)
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var callback = this.req.query[app.get('jsonp callback name')];
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof callback === 'string' && callback.length !== 0) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof callback === 'string' && callback.length !== 0) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (body === undefined) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (typeof body === 'string') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
} else if (typeof body === 'string') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
// the typeof check is just to reduce client error noise
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var body = statuses.message[statusCode] || String(statusCode)
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
* var uid = req.params.uid
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var done = callback;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var req = this.req;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var res = this;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var next = req.next;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var opts = options || {};
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof path !== 'string') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof path !== 'string') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof options === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof options === 'function') {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var pathname = encodeURI(path);
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var file = send(req, pathname, opts);
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (err && err.code === 'EISDIR') return next();
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var done = callback;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var name = filename;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var opts = options || null
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof filename === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof filename === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (typeof options === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
} else if (typeof options === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof filename === 'object' &&
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof filename === 'object' &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(typeof options === 'function' || options === undefined)) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
(typeof options === 'function' || options === undefined)) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var headers = {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var keys = Object.keys(opts.headers)
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
for (var i = 0; i < keys.length; i++) {
Use let instead of var in loops to avoid scope issues
info correctness var-in-loop
for (var i = 0; i < keys.length; i++) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var key = keys[i]
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (key.toLowerCase() !== 'content-disposition') {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var fullPath = !opts.root
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var ct = type.indexOf('/') === -1
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
var ct = type.indexOf('/') === -1
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var req = this.req;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var next = req.next;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var keys = Object.keys(obj)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
.filter(function (v) { return v !== 'default' })

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.