/*!1* express2* Copyright(c) 2009-2013 TJ Holowaychuk3* Copyright(c) 2014-2015 Douglas Christopher Wilson4* MIT Licensed5*/67'use strict';89/**10* Module dependencies.11* @api private12*/1314var contentDisposition = require('content-disposition');15var contentType = require('content-type');16var deprecate = require('depd')('express');17var flatten = require('array-flatten');18var mime = require('send').mime;19var basename = require('path').basename;20var etag = require('etag');21var proxyaddr = require('proxy-addr');22var qs = require('qs');23var querystring = require('querystring');2425/**26* Return strong ETag for `body`.27*28* @param {String|Buffer} body29* @param {String} [encoding]30* @return {String}31* @api private32*/3334exports.etag = function (body, encoding) {35var buf = !Buffer.isBuffer(body)36? new Buffer(body, encoding)37: body;3839return etag(buf, {weak: false});40};4142/**43* Return weak ETag for `body`.44*45* @param {String|Buffer} body46* @param {String} [encoding]47* @return {String}48* @api private49*/5051exports.wetag = function wetag(body, encoding){52var buf = !Buffer.isBuffer(body)53? new Buffer(body, encoding)54: body;5556return etag(buf, {weak: true});57};5859/**60* Check if `path` looks absolute.61*62* @param {String} path63* @return {Boolean}64* @api private65*/6667exports.isAbsolute = function(path){68if ('/' === path[0]) return true;69if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path70if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path71};7273/**74* Flatten the given `arr`.75*76* @param {Array} arr77* @return {Array}78* @api private79*/8081exports.flatten = deprecate.function(flatten,82'utils.flatten: use array-flatten npm module instead');8384/**85* Normalize the given `type`, for example "html" becomes "text/html".86*87* @param {String} type88* @return {Object}89* @api private90*/9192exports.normalizeType = function(type){93return ~type.indexOf('/')94? acceptParams(type)95: { value: mime.lookup(type), params: {} };96};9798/**99* Normalize `types`, for example "html" becomes "text/html".100*101* @param {Array} types102* @return {Array}103* @api private104*/105106exports.normalizeTypes = function(types){107var ret = [];108109for (var i = 0; i < types.length; ++i) {110ret.push(exports.normalizeType(types[i]));111}112113return ret;114};115116/**117* Generate Content-Disposition header appropriate for the filename.118* non-ascii filenames are urlencoded and a filename* parameter is added119*120* @param {String} filename121* @return {String}122* @api private123*/124125exports.contentDisposition = deprecate.function(contentDisposition,126'utils.contentDisposition: use content-disposition npm module instead');127128/**129* Parse accept params `str` returning an130* object with `.value`, `.quality` and `.params`.131* also includes `.originalIndex` for stable sorting132*133* @param {String} str134* @return {Object}135* @api private136*/137138function acceptParams(str, index) {139var parts = str.split(/ *; */);140var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };141142for (var i = 1; i < parts.length; ++i) {143var pms = parts[i].split(/ *= */);144if ('q' === pms[0]) {145ret.quality = parseFloat(pms[1]);146} else {147ret.params[pms[0]] = pms[1];148}149}150151return ret;152}153154/**155* Compile "etag" value to function.156*157* @param {Boolean|String|Function} val158* @return {Function}159* @api private160*/161162exports.compileETag = function(val) {163var fn;164165if (typeof val === 'function') {166return val;167}168169switch (val) {170case true:171fn = exports.wetag;172break;173case false:174break;175case 'strong':176fn = exports.etag;177break;178case 'weak':179fn = exports.wetag;180break;181default:182throw new TypeError('unknown value for etag function: ' + val);183}184185return fn;186}187188/**189* Compile "query parser" value to function.190*191* @param {String|Function} val192* @return {Function}193* @api private194*/195196exports.compileQueryParser = function compileQueryParser(val) {197var fn;198199if (typeof val === 'function') {200return val;201}202203switch (val) {204case true:205fn = querystring.parse;206break;207case false:208fn = newObject;209break;210case 'extended':211fn = parseExtendedQueryString;212break;213case 'simple':214fn = querystring.parse;215break;216default:217throw new TypeError('unknown value for query parser function: ' + val);218}219220return fn;221}222223/**224* Compile "proxy trust" value to function.225*226* @param {Boolean|String|Number|Array|Function} val227* @return {Function}228* @api private229*/230231exports.compileTrust = function(val) {232if (typeof val === 'function') return val;233234if (val === true) {235// Support plain true/false236return function(){ return true };237}238239if (typeof val === 'number') {240// Support trusting hop count241return function(a, i){ return i < val };242}243244if (typeof val === 'string') {245// Support comma-separated values246val = val.split(/ *, */);247}248249return proxyaddr.compile(val || []);250}251252/**253* Set the charset in a given Content-Type string.254*255* @param {String} type256* @param {String} charset257* @return {String}258* @api private259*/260261exports.setCharset = function setCharset(type, charset) {262if (!type || !charset) {263return type;264}265266// parse type267var parsed = contentType.parse(type);268269// set charset270parsed.parameters.charset = charset;271272// format type273return contentType.format(parsed);274};275276/**277* Parse an extended query string with qs.278*279* @return {Object}280* @private281*/282283function parseExtendedQueryString(str) {284return qs.parse(str, {285allowPrototypes: true286});287}288289/**290* Return new empty object.291*292* @return {Object}293* @api private294*/295296function newObject() {297return {};298}299300301