/ node_modules / express / lib / utils.js
utils.js
  1  /*!
  2   * express
  3   * Copyright(c) 2009-2013 TJ Holowaychuk
  4   * Copyright(c) 2014-2015 Douglas Christopher Wilson
  5   * MIT Licensed
  6   */
  7  
  8  'use strict';
  9  
 10  /**
 11   * Module dependencies.
 12   * @api private
 13   */
 14  
 15  var { METHODS } = require('node:http');
 16  var contentType = require('content-type');
 17  var etag = require('etag');
 18  var mime = require('mime-types')
 19  var proxyaddr = require('proxy-addr');
 20  var qs = require('qs');
 21  var querystring = require('querystring');
 22  
 23  /**
 24   * A list of lowercased HTTP methods that are supported by Node.js.
 25   * @api private
 26   */
 27  exports.methods = METHODS.map((method) => method.toLowerCase());
 28  
 29  /**
 30   * Return strong ETag for `body`.
 31   *
 32   * @param {String|Buffer} body
 33   * @param {String} [encoding]
 34   * @return {String}
 35   * @api private
 36   */
 37  
 38  exports.etag = createETagGenerator({ weak: false })
 39  
 40  /**
 41   * Return weak ETag for `body`.
 42   *
 43   * @param {String|Buffer} body
 44   * @param {String} [encoding]
 45   * @return {String}
 46   * @api private
 47   */
 48  
 49  exports.wetag = createETagGenerator({ weak: true })
 50  
 51  /**
 52   * Normalize the given `type`, for example "html" becomes "text/html".
 53   *
 54   * @param {String} type
 55   * @return {Object}
 56   * @api private
 57   */
 58  
 59  exports.normalizeType = function(type){
 60    return ~type.indexOf('/')
 61      ? acceptParams(type)
 62      : { value: (mime.lookup(type) || 'application/octet-stream'), params: {} }
 63  };
 64  
 65  /**
 66   * Normalize `types`, for example "html" becomes "text/html".
 67   *
 68   * @param {Array} types
 69   * @return {Array}
 70   * @api private
 71   */
 72  
 73  exports.normalizeTypes = function(types) {
 74    return types.map(exports.normalizeType);
 75  };
 76  
 77  
 78  /**
 79   * Parse accept params `str` returning an
 80   * object with `.value`, `.quality` and `.params`.
 81   *
 82   * @param {String} str
 83   * @return {Object}
 84   * @api private
 85   */
 86  
 87  function acceptParams (str) {
 88    var length = str.length;
 89    var colonIndex = str.indexOf(';');
 90    var index = colonIndex === -1 ? length : colonIndex;
 91    var ret = { value: str.slice(0, index).trim(), quality: 1, params: {} };
 92  
 93    while (index < length) {
 94      var splitIndex = str.indexOf('=', index);
 95      if (splitIndex === -1) break;
 96  
 97      var colonIndex = str.indexOf(';', index);
 98      var endIndex = colonIndex === -1 ? length : colonIndex;
 99  
100      if (splitIndex > endIndex) {
101        index = str.lastIndexOf(';', splitIndex - 1) + 1;
102        continue;
103      }
104  
105      var key = str.slice(index, splitIndex).trim();
106      var value = str.slice(splitIndex + 1, endIndex).trim();
107  
108      if (key === 'q') {
109        ret.quality = parseFloat(value);
110      } else {
111        ret.params[key] = value;
112      }
113  
114      index = endIndex + 1;
115    }
116  
117    return ret;
118  }
119  
120  /**
121   * Compile "etag" value to function.
122   *
123   * @param  {Boolean|String|Function} val
124   * @return {Function}
125   * @api private
126   */
127  
128  exports.compileETag = function(val) {
129    var fn;
130  
131    if (typeof val === 'function') {
132      return val;
133    }
134  
135    switch (val) {
136      case true:
137      case 'weak':
138        fn = exports.wetag;
139        break;
140      case false:
141        break;
142      case 'strong':
143        fn = exports.etag;
144        break;
145      default:
146        throw new TypeError('unknown value for etag function: ' + val);
147    }
148  
149    return fn;
150  }
151  
152  /**
153   * Compile "query parser" value to function.
154   *
155   * @param  {String|Function} val
156   * @return {Function}
157   * @api private
158   */
159  
160  exports.compileQueryParser = function compileQueryParser(val) {
161    var fn;
162  
163    if (typeof val === 'function') {
164      return val;
165    }
166  
167    switch (val) {
168      case true:
169      case 'simple':
170        fn = querystring.parse;
171        break;
172      case false:
173        break;
174      case 'extended':
175        fn = parseExtendedQueryString;
176        break;
177      default:
178        throw new TypeError('unknown value for query parser function: ' + val);
179    }
180  
181    return fn;
182  }
183  
184  /**
185   * Compile "proxy trust" value to function.
186   *
187   * @param  {Boolean|String|Number|Array|Function} val
188   * @return {Function}
189   * @api private
190   */
191  
192  exports.compileTrust = function(val) {
193    if (typeof val === 'function') return val;
194  
195    if (val === true) {
196      // Support plain true/false
197      return function(){ return true };
198    }
199  
200    if (typeof val === 'number') {
201      // Support trusting hop count
202      return function(a, i){ return i < val };
203    }
204  
205    if (typeof val === 'string') {
206      // Support comma-separated values
207      val = val.split(',')
208        .map(function (v) { return v.trim() })
209    }
210  
211    return proxyaddr.compile(val || []);
212  }
213  
214  /**
215   * Set the charset in a given Content-Type string.
216   *
217   * @param {String} type
218   * @param {String} charset
219   * @return {String}
220   * @api private
221   */
222  
223  exports.setCharset = function setCharset(type, charset) {
224    if (!type || !charset) {
225      return type;
226    }
227  
228    // parse type
229    var parsed = contentType.parse(type);
230  
231    // set charset
232    parsed.parameters.charset = charset;
233  
234    // format type
235    return contentType.format(parsed);
236  };
237  
238  /**
239   * Create an ETag generator function, generating ETags with
240   * the given options.
241   *
242   * @param {object} options
243   * @return {function}
244   * @private
245   */
246  
247  function createETagGenerator (options) {
248    return function generateETag (body, encoding) {
249      var buf = !Buffer.isBuffer(body)
250        ? Buffer.from(body, encoding)
251        : body
252  
253      return etag(buf, options)
254    }
255  }
256  
257  /**
258   * Parse an extended query string with qs.
259   *
260   * @param {String} str
261   * @return {Object}
262   * @private
263   */
264  
265  function parseExtendedQueryString(str) {
266    return qs.parse(str, {
267      allowPrototypes: true
268    });
269  }