Projekt

Obecné

Profil

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