/*!1* express2* Copyright(c) 2009-2013 TJ Holowaychuk3* Copyright(c) 2013 Roman Shtylman4* Copyright(c) 2014-2015 Douglas Christopher Wilson5* MIT Licensed6*/78'use strict';910/**11* Module dependencies.12* @private13*/1415var accepts = require('accepts');16var deprecate = require('depd')('express');17var isIP = require('net').isIP;18var typeis = require('type-is');19var http = require('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) {65if (!name) {66throw new TypeError('name argument is required to req.get');67}6869if (typeof name !== 'string') {70throw new TypeError('name must be a string to req.get');71}7273var lc = name.toLowerCase();7475switch (lc) {76case 'referer':77case 'referrer':78return this.headers.referrer79|| this.headers.referer;80default:81return this.headers[lc];82}83};8485/**86* To do: update docs.87*88* Check if the given `type(s)` is acceptable, returning89* the best match when true, otherwise `undefined`, in which90* case you should respond with 406 "Not Acceptable".91*92* The `type` value may be a single MIME type string93* such as "application/json", an extension name94* such as "json", a comma-delimited list such as "json, html, text/plain",95* an argument list such as `"json", "html", "text/plain"`,96* or an array `["json", "html", "text/plain"]`. When a list97* or array is given, the _best_ match, if any is returned.98*99* Examples:100*101* // Accept: text/html102* req.accepts('html');103* // => "html"104*105* // Accept: text/*, application/json106* req.accepts('html');107* // => "html"108* req.accepts('text/html');109* // => "text/html"110* req.accepts('json, text');111* // => "json"112* req.accepts('application/json');113* // => "application/json"114*115* // Accept: text/*, application/json116* req.accepts('image/png');117* req.accepts('png');118* // => undefined119*120* // Accept: text/*;q=.5, application/json121* req.accepts(['html', 'json']);122* req.accepts('html', 'json');123* req.accepts('html, json');124* // => "json"125*126* @param {String|Array} type(s)127* @return {String|Array|Boolean}128* @public129*/130131req.accepts = function(){132var accept = accepts(this);133return accept.types.apply(accept, arguments);134};135136/**137* Check if the given `encoding`s are accepted.138*139* @param {String} ...encoding140* @return {String|Array}141* @public142*/143144req.acceptsEncodings = function(){145var accept = accepts(this);146return accept.encodings.apply(accept, arguments);147};148149req.acceptsEncoding = deprecate.function(req.acceptsEncodings,150'req.acceptsEncoding: Use acceptsEncodings instead');151152/**153* Check if the given `charset`s are acceptable,154* otherwise you should respond with 406 "Not Acceptable".155*156* @param {String} ...charset157* @return {String|Array}158* @public159*/160161req.acceptsCharsets = function(){162var accept = accepts(this);163return accept.charsets.apply(accept, arguments);164};165166req.acceptsCharset = deprecate.function(req.acceptsCharsets,167'req.acceptsCharset: Use acceptsCharsets instead');168169/**170* Check if the given `lang`s are acceptable,171* otherwise you should respond with 406 "Not Acceptable".172*173* @param {String} ...lang174* @return {String|Array}175* @public176*/177178req.acceptsLanguages = function(){179var accept = accepts(this);180return accept.languages.apply(accept, arguments);181};182183req.acceptsLanguage = deprecate.function(req.acceptsLanguages,184'req.acceptsLanguage: Use acceptsLanguages instead');185186/**187* Parse Range header field, capping to the given `size`.188*189* Unspecified ranges such as "0-" require knowledge of your resource length. In190* the case of a byte range this is of course the total number of bytes. If the191* Range header field is not given `undefined` is returned, `-1` when unsatisfiable,192* and `-2` when syntactically invalid.193*194* When ranges are returned, the array has a "type" property which is the type of195* range that is required (most commonly, "bytes"). Each array element is an object196* with a "start" and "end" property for the portion of the range.197*198* The "combine" option can be set to `true` and overlapping & adjacent ranges199* will be combined into a single range.200*201* NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"202* should respond with 4 users when available, not 3.203*204* @param {number} size205* @param {object} [options]206* @param {boolean} [options.combine=false]207* @return {number|array}208* @public209*/210211req.range = function range(size, options) {212var range = this.get('Range');213if (!range) return;214return parseRange(size, range, options);215};216217/**218* Return the value of param `name` when present or `defaultValue`.219*220* - Checks route placeholders, ex: _/user/:id_221* - Checks body params, ex: id=12, {"id":12}222* - Checks query string params, ex: ?id=12223*224* To utilize request bodies, `req.body`225* should be an object. This can be done by using226* the `bodyParser()` middleware.227*228* @param {String} name229* @param {Mixed} [defaultValue]230* @return {String}231* @public232*/233234req.param = function param(name, defaultValue) {235var params = this.params || {};236var body = this.body || {};237var query = this.query || {};238239var args = arguments.length === 1240? 'name'241: 'name, default';242deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');243244if (null != params[name] && params.hasOwnProperty(name)) return params[name];245if (null != body[name]) return body[name];246if (null != query[name]) return query[name];247248return defaultValue;249};250251/**252* Check if the incoming request contains the "Content-Type"253* header field, and it contains the give mime `type`.254*255* Examples:256*257* // With Content-Type: text/html; charset=utf-8258* req.is('html');259* req.is('text/html');260* req.is('text/*');261* // => true262*263* // When Content-Type is application/json264* req.is('json');265* req.is('application/json');266* req.is('application/*');267* // => true268*269* req.is('html');270* // => false271*272* @param {String|Array} types...273* @return {String|false|null}274* @public275*/276277req.is = function is(types) {278var arr = types;279280// support flattened arguments281if (!Array.isArray(types)) {282arr = new Array(arguments.length);283for (var i = 0; i < arr.length; i++) {284arr[i] = arguments[i];285}286}287288return typeis(this, arr);289};290291/**292* Return the protocol string "http" or "https"293* when requested with TLS. When the "trust proxy"294* setting trusts the socket address, the295* "X-Forwarded-Proto" header field will be trusted296* and used if present.297*298* If you're running behind a reverse proxy that299* supplies https for you this may be enabled.300*301* @return {String}302* @public303*/304305defineGetter(req, 'protocol', function protocol(){306var proto = this.connection.encrypted307? 'https'308: 'http';309var trust = this.app.get('trust proxy fn');310311if (!trust(this.connection.remoteAddress, 0)) {312return proto;313}314315// Note: X-Forwarded-Proto is normally only ever a316// single value, but this is to be safe.317proto = this.get('X-Forwarded-Proto') || proto;318return proto.split(/\s*,\s*/)[0];319});320321/**322* Short-hand for:323*324* req.protocol === 'https'325*326* @return {Boolean}327* @public328*/329330defineGetter(req, 'secure', function secure(){331return this.protocol === 'https';332});333334/**335* Return the remote address from the trusted proxy.336*337* The is the remote address on the socket unless338* "trust proxy" is set.339*340* @return {String}341* @public342*/343344defineGetter(req, 'ip', function ip(){345var trust = this.app.get('trust proxy fn');346return proxyaddr(this, trust);347});348349/**350* When "trust proxy" is set, trusted proxy addresses + client.351*352* For example if the value were "client, proxy1, proxy2"353* you would receive the array `["client", "proxy1", "proxy2"]`354* where "proxy2" is the furthest down-stream and "proxy1" and355* "proxy2" were trusted.356*357* @return {Array}358* @public359*/360361defineGetter(req, 'ips', function ips() {362var trust = this.app.get('trust proxy fn');363var addrs = proxyaddr.all(this, trust);364365// reverse the order (to farthest -> closest)366// and remove socket address367addrs.reverse().pop()368369return addrs370});371372/**373* Return subdomains as an array.374*375* Subdomains are the dot-separated parts of the host before the main domain of376* the app. By default, the domain of the app is assumed to be the last two377* parts of the host. This can be changed by setting "subdomain offset".378*379* For example, if the domain is "tobi.ferrets.example.com":380* If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.381* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.382*383* @return {Array}384* @public385*/386387defineGetter(req, 'subdomains', function subdomains() {388var hostname = this.hostname;389390if (!hostname) return [];391392var offset = this.app.get('subdomain offset');393var subdomains = !isIP(hostname)394? hostname.split('.').reverse()395: [hostname];396397return subdomains.slice(offset);398});399400/**401* Short-hand for `url.parse(req.url).pathname`.402*403* @return {String}404* @public405*/406407defineGetter(req, 'path', function path() {408return parse(this).pathname;409});410411/**412* Parse the "Host" header field to a hostname.413*414* When the "trust proxy" setting trusts the socket415* address, the "X-Forwarded-Host" header field will416* be trusted.417*418* @return {String}419* @public420*/421422defineGetter(req, 'hostname', function hostname(){423var trust = this.app.get('trust proxy fn');424var host = this.get('X-Forwarded-Host');425426if (!host || !trust(this.connection.remoteAddress, 0)) {427host = this.get('Host');428}429430if (!host) return;431432// IPv6 literal support433var offset = host[0] === '['434? host.indexOf(']') + 1435: 0;436var index = host.indexOf(':', offset);437438return index !== -1439? host.substring(0, index)440: host;441});442443// TODO: change req.host to return host in next major444445defineGetter(req, 'host', deprecate.function(function host(){446return this.hostname;447}, 'req.host: Use req.hostname instead'));448449/**450* Check if the request is fresh, aka451* Last-Modified and/or the ETag452* still match.453*454* @return {Boolean}455* @public456*/457458defineGetter(req, 'fresh', function(){459var method = this.method;460var res = this.res461var status = res.statusCode462463// GET or HEAD for weak freshness validation only464if ('GET' !== method && 'HEAD' !== method) return false;465466// 2xx or 304 as per rfc2616 14.26467if ((status >= 200 && status < 300) || 304 === status) {468return fresh(this.headers, {469'etag': res.get('ETag'),470'last-modified': res.get('Last-Modified')471})472}473474return false;475});476477/**478* Check if the request is stale, aka479* "Last-Modified" and / or the "ETag" for the480* resource has changed.481*482* @return {Boolean}483* @public484*/485486defineGetter(req, 'stale', function stale(){487return !this.fresh;488});489490/**491* Check if the request was an _XMLHttpRequest_.492*493* @return {Boolean}494* @public495*/496497defineGetter(req, 'xhr', function xhr(){498var val = this.get('X-Requested-With') || '';499return val.toLowerCase() === 'xmlhttprequest';500});501502/**503* Helper function for creating a getter on an object.504*505* @param {Object} obj506* @param {String} name507* @param {Function} getter508* @private509*/510function defineGetter(obj, name, getter) {511Object.defineProperty(obj, name, {512configurable: true,513enumerable: true,514get: getter515});516}517518519