lib/request.js JAVASCRIPT 528 lines View on github.com → Search inside
1/*!2 * express3 * Copyright(c) 2009-2013 TJ Holowaychuk4 * Copyright(c) 2013 Roman Shtylman5 * Copyright(c) 2014-2015 Douglas Christopher Wilson6 * MIT Licensed7 */89'use strict';1011/**12 * Module dependencies.13 * @private14 */1516var accepts = require('accepts');17var isIP = require('node:net').isIP;18var typeis = require('type-is');19var http = require('node:http');20var fresh = require('fresh');21var parseRange = require('range-parser');22var parse = require('parseurl');23var proxyaddr = require('proxy-addr');2425/**26 * Request prototype.27 * @public28 */2930var req = Object.create(http.IncomingMessage.prototype)3132/**33 * Module exports.34 * @public35 */3637module.exports = req3839/**40 * Return request header.41 *42 * The `Referrer` header field is special-cased,43 * both `Referrer` and `Referer` are interchangeable.44 *45 * Examples:46 *47 *     req.get('Content-Type');48 *     // => "text/plain"49 *50 *     req.get('content-type');51 *     // => "text/plain"52 *53 *     req.get('Something');54 *     // => undefined55 *56 * Aliased as `req.header()`.57 *58 * @param {String} name59 * @return {String}60 * @public61 */6263req.get =64req.header = function header(name) {65  if (!name) {66    throw new TypeError('name argument is required to req.get');67  }6869  if (typeof name !== 'string') {70    throw new TypeError('name must be a string to req.get');71  }7273  var lc = name.toLowerCase();7475  switch (lc) {76    case 'referer':77    case 'referrer':78      return this.headers.referrer79        || this.headers.referer;80    default:81      return this.headers[lc];82  }83};8485/**86 * Check if the given `type(s)` is acceptable, returning87 * the best match when true, otherwise `false`, in which88 * case you should respond with 406 "Not Acceptable".89 *90 * The `type` value may be a single MIME type string91 * such as "application/json", an extension name92 * such as "json", an argument list such as `"json", "html", "text/plain"`,93 * or an array `["json", "html", "text/plain"]`. When a list94 * or array is given, the _best_ match, if any is returned.95 *96 * Examples:97 *98 *     // Accept: text/html99 *     req.accepts('html');100 *     // => "html"101 *102 *     // Accept: text/*, application/json103 *     req.accepts('html');104 *     // => "html"105 *     req.accepts('text/html');106 *     // => "text/html"107 *     req.accepts('json', 'text');108 *     // => "json"109 *     req.accepts('application/json');110 *     // => "application/json"111 *112 *     // Accept: text/*, application/json113 *     req.accepts('image/png');114 *     req.accepts('png');115 *     // => false116 *117 *     // Accept: text/*;q=.5, application/json118 *     req.accepts(['html', 'json']);119 *     req.accepts('html', 'json');120 *     // => "json"121 *122 * @param {String|Array} type(s)123 * @return {String|Array|Boolean}124 * @public125 */126127req.accepts = function(){128  var accept = accepts(this);129  return accept.types.apply(accept, arguments);130};131132/**133 * Check if the given `encoding`s are accepted.134 *135 * @param {String} ...encoding136 * @return {String|Array}137 * @public138 */139140req.acceptsEncodings = function(){141  var accept = accepts(this);142  return accept.encodings.apply(accept, arguments);143};144145/**146 * Checks if the specified `charset`s are acceptable based on the request's `Accept-Charset` header.147 * Returns the best matching charset or an array of acceptable charsets.148 *149 * The `charset` argument(s) can be:150 * - A single charset string (e.g., "utf-8")151 * - Multiple charset strings as arguments (e.g., `"utf-8", "iso-8859-1"`)152 * - A comma-delimited list of charsets (e.g., `"utf-8, iso-8859-1"`)153 *154 * Examples:155 *156 *     // Accept-Charset: utf-8, iso-8859-1157 *     req.acceptsCharsets('utf-8');158 *     // => "utf-8"159 *160 *     req.acceptsCharsets('utf-8', 'iso-8859-1');161 *     // => "utf-8"162 *163 *     req.acceptsCharsets('utf-8, utf-16');164 *     // => "utf-8"165 *166 * @param {...String} charsets - The charset(s) to check against the `Accept-Charset` header.167 * @return {String|Array} - The best matching charset, or an array of acceptable charsets.168 * @public169 */170171req.acceptsCharsets = function(...charsets) {172  const accept = accepts(this);173  return accept.charsets(...charsets);174};175176/**177 * Check if the given `lang`s are acceptable,178 * otherwise you should respond with 406 "Not Acceptable".179 *180 * @param {String} ...lang181 * @return {String|Array}182 * @public183 */184185req.acceptsLanguages = function(...languages) {186  return accepts(this).languages(...languages);187};188189/**190 * Parse Range header field, capping to the given `size`.191 *192 * Unspecified ranges such as "0-" require knowledge of your resource length. In193 * the case of a byte range this is of course the total number of bytes. If the194 * Range header field is not given `undefined` is returned, `-1` when unsatisfiable,195 * and `-2` when syntactically invalid.196 *197 * When ranges are returned, the array has a "type" property which is the type of198 * range that is required (most commonly, "bytes"). Each array element is an object199 * with a "start" and "end" property for the portion of the range.200 *201 * The "combine" option can be set to `true` and overlapping & adjacent ranges202 * will be combined into a single range.203 *204 * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"205 * should respond with 4 users when available, not 3.206 *207 * @param {number} size208 * @param {object} [options]209 * @param {boolean} [options.combine=false]210 * @return {number|array}211 * @public212 */213214req.range = function range(size, options) {215  var range = this.get('Range');216  if (!range) return;217  return parseRange(size, range, options);218};219220/**221 * Parse the query string of `req.url`.222 *223 * This uses the "query parser" setting to parse the raw224 * string into an object.225 *226 * @return {String}227 * @api public228 */229230defineGetter(req, 'query', function query(){231  var queryparse = this.app.get('query parser fn');232233  if (!queryparse) {234    // parsing is disabled235    return Object.create(null);236  }237238  var querystring = parse(this).query;239240  return queryparse(querystring);241});242243/**244 * Check if the incoming request contains the "Content-Type"245 * header field, and it contains the given mime `type`.246 *247 * Examples:248 *249 *      // With Content-Type: text/html; charset=utf-8250 *      req.is('html');251 *      req.is('text/html');252 *      req.is('text/*');253 *      // => true254 *255 *      // When Content-Type is application/json256 *      req.is('json');257 *      req.is('application/json');258 *      req.is('application/*');259 *      // => true260 *261 *      req.is('html');262 *      // => false263 *264 * @param {String|Array} types...265 * @return {String|false|null}266 * @public267 */268269req.is = function is(types) {270  var arr = types;271272  // support flattened arguments273  if (!Array.isArray(types)) {274    arr = new Array(arguments.length);275    for (var i = 0; i < arr.length; i++) {276      arr[i] = arguments[i];277    }278  }279280  return typeis(this, arr);281};282283/**284 * Return the protocol string "http" or "https"285 * when requested with TLS. When the "trust proxy"286 * setting trusts the socket address, the287 * "X-Forwarded-Proto" header field will be trusted288 * and used if present.289 *290 * If you're running behind a reverse proxy that291 * supplies https for you this may be enabled.292 *293 * @return {String}294 * @public295 */296297defineGetter(req, 'protocol', function protocol(){298  var proto = this.socket.encrypted299    ? 'https'300    : 'http';301  var trust = this.app.get('trust proxy fn');302303  if (!trust(this.socket.remoteAddress, 0)) {304    return proto;305  }306307  // Note: X-Forwarded-Proto is normally only ever a308  //       single value, but this is to be safe.309  var header = this.get('X-Forwarded-Proto') || proto310  var index = header.indexOf(',')311312  return index !== -1313    ? header.substring(0, index).trim()314    : header.trim()315});316317/**318 * Short-hand for:319 *320 *    req.protocol === 'https'321 *322 * @return {Boolean}323 * @public324 */325326defineGetter(req, 'secure', function secure(){327  return this.protocol === 'https';328});329330/**331 * Return the remote address from the trusted proxy.332 *333 * The is the remote address on the socket unless334 * "trust proxy" is set.335 *336 * @return {String}337 * @public338 */339340defineGetter(req, 'ip', function ip(){341  var trust = this.app.get('trust proxy fn');342  return proxyaddr(this, trust);343});344345/**346 * When "trust proxy" is set, trusted proxy addresses + client.347 *348 * For example if the value were "client, proxy1, proxy2"349 * you would receive the array `["client", "proxy1", "proxy2"]`350 * where "proxy2" is the furthest down-stream and "proxy1" and351 * "proxy2" were trusted.352 *353 * @return {Array}354 * @public355 */356357defineGetter(req, 'ips', function ips() {358  var trust = this.app.get('trust proxy fn');359  var addrs = proxyaddr.all(this, trust);360361  // reverse the order (to farthest -> closest)362  // and remove socket address363  addrs.reverse().pop()364365  return addrs366});367368/**369 * Return subdomains as an array.370 *371 * Subdomains are the dot-separated parts of the host before the main domain of372 * the app. By default, the domain of the app is assumed to be the last two373 * parts of the host. This can be changed by setting "subdomain offset".374 *375 * For example, if the domain is "tobi.ferrets.example.com":376 * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.377 * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.378 *379 * @return {Array}380 * @public381 */382383defineGetter(req, 'subdomains', function subdomains() {384  var hostname = this.hostname;385386  if (!hostname) return [];387388  var offset = this.app.get('subdomain offset');389  var subdomains = !isIP(hostname)390    ? hostname.split('.').reverse()391    : [hostname];392393  return subdomains.slice(offset);394});395396/**397 * Short-hand for `url.parse(req.url).pathname`.398 *399 * @return {String}400 * @public401 */402403defineGetter(req, 'path', function path() {404  return parse(this).pathname;405});406407/**408 * Parse the "Host" header field to a host.409 *410 * When the "trust proxy" setting trusts the socket411 * address, the "X-Forwarded-Host" header field will412 * be trusted.413 *414 * @return {String}415 * @public416 */417418defineGetter(req, 'host', function host(){419  var trust = this.app.get('trust proxy fn');420  var val = this.get('X-Forwarded-Host');421422  if (!val || !trust(this.socket.remoteAddress, 0)) {423    val = this.get('Host');424  } else if (val.indexOf(',') !== -1) {425    // Note: X-Forwarded-Host is normally only ever a426    //       single value, but this is to be safe.427    val = val.substring(0, val.indexOf(',')).trimRight()428  }429430  return val || undefined;431});432433/**434 * Parse the "Host" header field to a hostname.435 *436 * When the "trust proxy" setting trusts the socket437 * address, the "X-Forwarded-Host" header field will438 * be trusted.439 *440 * @return {String}441 * @api public442 */443444defineGetter(req, 'hostname', function hostname(){445  var host = this.host;446447  if (!host) return;448449  // IPv6 literal support450  var offset = host[0] === '['451    ? host.indexOf(']') + 1452    : 0;453  var index = host.indexOf(':', offset);454455  return index !== -1456    ? host.substring(0, index)457    : host;458});459460/**461 * Check if the request is fresh, aka462 * Last-Modified or the ETag463 * still match.464 *465 * @return {Boolean}466 * @public467 */468469defineGetter(req, 'fresh', function(){470  var method = this.method;471  var res = this.res472  var status = res.statusCode473474  // GET or HEAD for weak freshness validation only475  if ('GET' !== method && 'HEAD' !== method) return false;476477  // 2xx or 304 as per rfc2616 14.26478  if ((status >= 200 && status < 300) || 304 === status) {479    return fresh(this.headers, {480      'etag': res.get('ETag'),481      'last-modified': res.get('Last-Modified')482    })483  }484485  return false;486});487488/**489 * Check if the request is stale, aka490 * "Last-Modified" and / or the "ETag" for the491 * resource has changed.492 *493 * @return {Boolean}494 * @public495 */496497defineGetter(req, 'stale', function stale(){498  return !this.fresh;499});500501/**502 * Check if the request was an _XMLHttpRequest_.503 *504 * @return {Boolean}505 * @public506 */507508defineGetter(req, 'xhr', function xhr(){509  var val = this.get('X-Requested-With') || '';510  return val.toLowerCase() === 'xmlhttprequest';511});512513/**514 * Helper function for creating a getter on an object.515 *516 * @param {Object} obj517 * @param {String} name518 * @param {Function} getter519 * @private520 */521function defineGetter(obj, name, getter) {522  Object.defineProperty(obj, name, {523    configurable: true,524    enumerable: true,525    get: getter526  });527}

Code quality findings 48

Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var accepts = require('accepts');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var isIP = require('node:net').isIP;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var typeis = require('type-is');
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 fresh = require('fresh');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var parseRange = require('range-parser');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var parse = require('parseurl');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var proxyaddr = require('proxy-addr');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var req = Object.create(http.IncomingMessage.prototype)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof name !== 'string') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof name !== 'string') {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var lc = name.toLowerCase();
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var accept = accepts(this);
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var accept = accepts(this);
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var range = this.get('Range');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var queryparse = this.app.get('query parser fn');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var querystring = parse(this).query;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var arr = types;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
for (var i = 0; i < arr.length; i++) {
Use let instead of var in loops to avoid scope issues
info correctness var-in-loop
for (var i = 0; i < arr.length; i++) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var proto = this.socket.encrypted
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var trust = this.app.get('trust proxy fn');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var header = this.get('X-Forwarded-Proto') || proto
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var index = header.indexOf(',')
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return index !== -1
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
* req.protocol === 'https'
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return this.protocol === 'https';
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var trust = this.app.get('trust proxy fn');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var trust = this.app.get('trust proxy fn');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var addrs = proxyaddr.all(this, trust);
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var hostname = this.hostname;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var offset = this.app.get('subdomain offset');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var subdomains = !isIP(hostname)
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var trust = this.app.get('trust proxy fn');
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var val = this.get('X-Forwarded-Host');
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (val.indexOf(',') !== -1) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var host = this.host;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var offset = host[0] === '['
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
var offset = host[0] === '['
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var index = host.indexOf(':', offset);
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return index !== -1
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var method = this.method;
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var res = this.res
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var status = res.statusCode
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ('GET' !== method && 'HEAD' !== method) return false;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((status >= 200 && status < 300) || 304 === status) {
Use let or const to avoid scope issues and hoisting
info correctness var-declaration
var val = this.get('X-Requested-With') || '';
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return val.toLowerCase() === 'xmlhttprequest';

Get this view in your editor

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