You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
5.3 KiB

7 years ago
  1. /**
  2. * Module dependencies.
  3. */
  4. var contentDisposition = require('content-disposition');
  5. var deprecate = require('depd')('express');
  6. var mime = require('send').mime;
  7. var basename = require('path').basename;
  8. var etag = require('etag');
  9. var proxyaddr = require('proxy-addr');
  10. var qs = require('qs');
  11. var querystring = require('querystring');
  12. var typer = require('media-typer');
  13. /**
  14. * Return strong ETag for `body`.
  15. *
  16. * @param {String|Buffer} body
  17. * @param {String} [encoding]
  18. * @return {String}
  19. * @api private
  20. */
  21. exports.etag = function (body, encoding) {
  22. var buf = !Buffer.isBuffer(body)
  23. ? new Buffer(body, encoding)
  24. : body;
  25. return etag(buf, {weak: false});
  26. };
  27. /**
  28. * Return weak ETag for `body`.
  29. *
  30. * @param {String|Buffer} body
  31. * @param {String} [encoding]
  32. * @return {String}
  33. * @api private
  34. */
  35. exports.wetag = function wetag(body, encoding){
  36. var buf = !Buffer.isBuffer(body)
  37. ? new Buffer(body, encoding)
  38. : body;
  39. return etag(buf, {weak: true});
  40. };
  41. /**
  42. * Check if `path` looks absolute.
  43. *
  44. * @param {String} path
  45. * @return {Boolean}
  46. * @api private
  47. */
  48. exports.isAbsolute = function(path){
  49. if ('/' == path[0]) return true;
  50. if (':' == path[1] && '\\' == path[2]) return true;
  51. if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
  52. };
  53. /**
  54. * Flatten the given `arr`.
  55. *
  56. * @param {Array} arr
  57. * @return {Array}
  58. * @api private
  59. */
  60. exports.flatten = function(arr, ret){
  61. ret = ret || [];
  62. var len = arr.length;
  63. for (var i = 0; i < len; ++i) {
  64. if (Array.isArray(arr[i])) {
  65. exports.flatten(arr[i], ret);
  66. } else {
  67. ret.push(arr[i]);
  68. }
  69. }
  70. return ret;
  71. };
  72. /**
  73. * Normalize the given `type`, for example "html" becomes "text/html".
  74. *
  75. * @param {String} type
  76. * @return {Object}
  77. * @api private
  78. */
  79. exports.normalizeType = function(type){
  80. return ~type.indexOf('/')
  81. ? acceptParams(type)
  82. : { value: mime.lookup(type), params: {} };
  83. };
  84. /**
  85. * Normalize `types`, for example "html" becomes "text/html".
  86. *
  87. * @param {Array} types
  88. * @return {Array}
  89. * @api private
  90. */
  91. exports.normalizeTypes = function(types){
  92. var ret = [];
  93. for (var i = 0; i < types.length; ++i) {
  94. ret.push(exports.normalizeType(types[i]));
  95. }
  96. return ret;
  97. };
  98. /**
  99. * Generate Content-Disposition header appropriate for the filename.
  100. * non-ascii filenames are urlencoded and a filename* parameter is added
  101. *
  102. * @param {String} filename
  103. * @return {String}
  104. * @api private
  105. */
  106. exports.contentDisposition = deprecate.function(contentDisposition,
  107. 'utils.contentDisposition: use content-disposition npm module instead');
  108. /**
  109. * Parse accept params `str` returning an
  110. * object with `.value`, `.quality` and `.params`.
  111. * also includes `.originalIndex` for stable sorting
  112. *
  113. * @param {String} str
  114. * @return {Object}
  115. * @api private
  116. */
  117. function acceptParams(str, index) {
  118. var parts = str.split(/ *; */);
  119. var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
  120. for (var i = 1; i < parts.length; ++i) {
  121. var pms = parts[i].split(/ *= */);
  122. if ('q' == pms[0]) {
  123. ret.quality = parseFloat(pms[1]);
  124. } else {
  125. ret.params[pms[0]] = pms[1];
  126. }
  127. }
  128. return ret;
  129. }
  130. /**
  131. * Compile "etag" value to function.
  132. *
  133. * @param {Boolean|String|Function} val
  134. * @return {Function}
  135. * @api private
  136. */
  137. exports.compileETag = function(val) {
  138. var fn;
  139. if (typeof val === 'function') {
  140. return val;
  141. }
  142. switch (val) {
  143. case true:
  144. fn = exports.wetag;
  145. break;
  146. case false:
  147. break;
  148. case 'strong':
  149. fn = exports.etag;
  150. break;
  151. case 'weak':
  152. fn = exports.wetag;
  153. break;
  154. default:
  155. throw new TypeError('unknown value for etag function: ' + val);
  156. }
  157. return fn;
  158. }
  159. /**
  160. * Compile "query parser" value to function.
  161. *
  162. * @param {String|Function} val
  163. * @return {Function}
  164. * @api private
  165. */
  166. exports.compileQueryParser = function compileQueryParser(val) {
  167. var fn;
  168. if (typeof val === 'function') {
  169. return val;
  170. }
  171. switch (val) {
  172. case true:
  173. fn = querystring.parse;
  174. break;
  175. case false:
  176. fn = newObject;
  177. break;
  178. case 'extended':
  179. fn = qs.parse;
  180. break;
  181. case 'simple':
  182. fn = querystring.parse;
  183. break;
  184. default:
  185. throw new TypeError('unknown value for query parser function: ' + val);
  186. }
  187. return fn;
  188. }
  189. /**
  190. * Compile "proxy trust" value to function.
  191. *
  192. * @param {Boolean|String|Number|Array|Function} val
  193. * @return {Function}
  194. * @api private
  195. */
  196. exports.compileTrust = function(val) {
  197. if (typeof val === 'function') return val;
  198. if (val === true) {
  199. // Support plain true/false
  200. return function(){ return true };
  201. }
  202. if (typeof val === 'number') {
  203. // Support trusting hop count
  204. return function(a, i){ return i < val };
  205. }
  206. if (typeof val === 'string') {
  207. // Support comma-separated values
  208. val = val.split(/ *, */);
  209. }
  210. return proxyaddr.compile(val || []);
  211. }
  212. /**
  213. * Set the charset in a given Content-Type string.
  214. *
  215. * @param {String} type
  216. * @param {String} charset
  217. * @return {String}
  218. * @api private
  219. */
  220. exports.setCharset = function(type, charset){
  221. if (!type || !charset) return type;
  222. // parse type
  223. var parsed = typer.parse(type);
  224. // set charset
  225. parsed.parameters.charset = charset;
  226. // format type
  227. return typer.format(parsed);
  228. };
  229. /**
  230. * Return new empty objet.
  231. *
  232. * @return {Object}
  233. * @api private
  234. */
  235. function newObject() {
  236. return {};
  237. }