UNPKG

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