UNPKG

9.41 kBJavaScriptView Raw
1
2/*!
3 * Connect - utils
4 * Copyright(c) 2010 Sencha Inc.
5 * Copyright(c) 2011 TJ Holowaychuk
6 * Copyright(c) 2014-2015 Douglas Christopher Wilson
7 * MIT Licensed
8 */
9
10/**
11 * Module dependencies.
12 */
13
14var bytes = require('bytes');
15var contentType = require('content-type');
16var cookieParser = require('cookie-parser');
17var createError = require('http-errors');
18var deprecate = require('depd')('connect');
19var http = require('http')
20 , crypto = require('crypto')
21 , parseurl = require('parseurl')
22 , sep = require('path').sep
23 , signature = require('cookie-signature')
24 , typeis = require('type-is')
25 , nodeVersion = process.versions.node.split('.');
26var merge = require('utils-merge');
27
28/**
29 * pause is broken in node < 0.10
30 */
31exports.brokenPause = parseInt(nodeVersion[0], 10) === 0
32 && parseInt(nodeVersion[1], 10) < 10;
33
34/**
35 * Return `true` if the request has a body, otherwise return `false`.
36 *
37 * @param {IncomingMessage} req
38 * @return {Boolean}
39 * @api private
40 */
41
42exports.hasBody = deprecate.function(typeis.hasBody,
43 'utils.hasBody: use type-is npm module instead');
44
45/**
46 * Extract the mime type from the given request's
47 * _Content-Type_ header.
48 *
49 * @param {IncomingMessage} req
50 * @return {String}
51 * @api private
52 */
53
54exports.mime = function(req) {
55 var str = req.headers['content-type'] || ''
56 , i = str.indexOf(';');
57 return ~i ? str.slice(0, i) : str;
58};
59
60exports.mime = deprecate.function(exports.mime,
61 'utils.mime: use type-is npm module instead for mime comparisons');
62
63/**
64 * Generate an `Error` from the given status `code`
65 * and optional `msg`.
66 *
67 * @param {Number} code
68 * @param {String} msg
69 * @return {Error}
70 * @api private
71 */
72
73exports.error = function(code, msg){
74 var err = new Error(msg || http.STATUS_CODES[code]);
75 err.status = code;
76 return err;
77};
78
79exports.error = deprecate.function(exports.error,
80 'utils.error: use http-errors npm module instead');
81
82/**
83 * Return md5 hash of the given string and optional encoding,
84 * defaulting to hex.
85 *
86 * utils.md5('wahoo');
87 * // => "e493298061761236c96b02ea6aa8a2ad"
88 *
89 * @param {String} str
90 * @param {String} encoding
91 * @return {String}
92 * @api private
93 */
94
95exports.md5 = function(str, encoding){
96 return crypto
97 .createHash('md5')
98 .update(str, 'utf8')
99 .digest(encoding || 'hex');
100};
101
102exports.md5 = deprecate.function(exports.md5,
103 'utils.md5: use crypto npm module instead for hashing');
104
105/**
106 * Merge object b with object a.
107 *
108 * var a = { foo: 'bar' }
109 * , b = { bar: 'baz' };
110 *
111 * utils.merge(a, b);
112 * // => { foo: 'bar', bar: 'baz' }
113 *
114 * @param {Object} a
115 * @param {Object} b
116 * @return {Object}
117 * @api private
118 */
119
120exports.merge = deprecate.function(merge,
121 'utils.merge: use utils-merge npm module instead');
122
123/**
124 * Escape the given string of `html`.
125 *
126 * @param {String} html
127 * @return {String}
128 * @api private
129 */
130
131exports.escape = function(html){
132 return String(html)
133 .replace(/&(?!\w+;)/g, '&amp;')
134 .replace(/</g, '&lt;')
135 .replace(/>/g, '&gt;')
136 .replace(/"/g, '&quot;');
137};
138
139exports.escape = deprecate.function(exports.escape,
140 'utils.escape: use escape-html npm module instead');
141
142/**
143 * Sign the given `val` with `secret`.
144 *
145 * @param {String} val
146 * @param {String} secret
147 * @return {String}
148 * @api private
149 */
150
151exports.sign = deprecate.function(signature.sign,
152 'utils.sign: use cookie-signature npm module instead');
153
154/**
155 * Unsign and decode the given `val` with `secret`,
156 * returning `false` if the signature is invalid.
157 *
158 * @param {String} val
159 * @param {String} secret
160 * @return {String|Boolean}
161 * @api private
162 */
163
164exports.unsign = deprecate.function(signature.unsign,
165 'utils.unsign: use cookie-signature npm module instead');
166
167/**
168 * Parse signed cookies, returning an object
169 * containing the decoded key/value pairs,
170 * while removing the signed key from `obj`.
171 *
172 * @param {Object} obj
173 * @return {Object}
174 * @api private
175 */
176
177exports.parseSignedCookies = deprecate.function(cookieParser.signedCookies,
178 'utils.parseSignedCookies: use cookie-parser npm module instead');
179
180/**
181 * Parse a signed cookie string, return the decoded value
182 *
183 * @param {String} str signed cookie string
184 * @param {String} secret
185 * @return {String} decoded value
186 * @api private
187 */
188
189exports.parseSignedCookie = deprecate.function(cookieParser.signedCookie,
190 'utils.parseSignedCookie: use cookie-parser npm module instead');
191
192/**
193 * Parse JSON cookies.
194 *
195 * @param {Object} obj
196 * @return {Object}
197 * @api private
198 */
199
200exports.parseJSONCookies = deprecate.function(cookieParser.JSONCookies,
201 'utils.parseJSONCookies: use cookie-parser npm module instead');
202
203/**
204 * Parse JSON cookie string
205 *
206 * @param {String} str
207 * @return {Object} Parsed object or null if not json cookie
208 * @api private
209 */
210
211exports.parseJSONCookie = deprecate.function(cookieParser.JSONCookie,
212 'utils.parseJSONCookie: use cookie-parser npm module instead');
213
214/**
215 * Pause `data` and `end` events on the given `obj`.
216 * Middleware performing async tasks _should_ utilize
217 * this utility (or similar), to re-emit data once
218 * the async operation has completed, otherwise these
219 * events may be lost. Pause is only required for
220 * node versions less than 10, and is replaced with
221 * noop's otherwise.
222 *
223 * var pause = utils.pause(req);
224 * fs.readFile(path, function(){
225 * next();
226 * pause.resume();
227 * });
228 *
229 * @param {Object} obj
230 * @return {Object}
231 * @api private
232 */
233
234exports.pause = exports.brokenPause
235 ? require('pause')
236 : function () {
237 return {
238 end: noop,
239 resume: noop
240 }
241 }
242
243/**
244 * Strip `Content-*` headers from `res`.
245 *
246 * @param {ServerResponse} res
247 * @api private
248 */
249
250exports.removeContentHeaders = function(res){
251 if (!res._headers) return;
252 Object.keys(res._headers).forEach(function(field){
253 if (0 == field.indexOf('content')) {
254 res.removeHeader(field);
255 }
256 });
257};
258
259exports.removeContentHeaders = deprecate.function(exports.removeContentHeaders,
260 'utils.removeContentHeaders: this private api moved with serve-static');
261
262/**
263 * Check if `req` is a conditional GET request.
264 *
265 * @param {IncomingMessage} req
266 * @return {Boolean}
267 * @api private
268 */
269
270exports.conditionalGET = function(req) {
271 return req.headers['if-modified-since']
272 || req.headers['if-none-match'];
273};
274
275exports.conditionalGET = deprecate.function(exports.conditionalGET,
276 'utils.conditionalGET: use fresh npm module instead');
277
278/**
279 * Respond with 401 "Unauthorized".
280 *
281 * @param {ServerResponse} res
282 * @param {String} realm
283 * @api private
284 */
285
286exports.unauthorized = function(res, realm) {
287 res.statusCode = 401;
288 res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
289 res.end('Unauthorized');
290};
291
292exports.unauthorized = deprecate.function(exports.unauthorized,
293 'utils.unauthorized: this private api moved with basic-auth-connect');
294
295/**
296 * Respond with 304 "Not Modified".
297 *
298 * @param {ServerResponse} res
299 * @param {Object} headers
300 * @api private
301 */
302
303exports.notModified = function(res) {
304 exports.removeContentHeaders(res);
305 res.statusCode = 304;
306 res.end();
307};
308
309exports.notModified = deprecate.function(exports.notModified,
310 'utils.notModified: this private api moved with serve-static');
311
312/**
313 * Return an ETag in the form of `"<size>-<mtime>"`
314 * from the given `stat`.
315 *
316 * @param {Object} stat
317 * @return {String}
318 * @api private
319 */
320
321exports.etag = function(stat) {
322 return '"' + stat.size + '-' + Number(stat.mtime) + '"';
323};
324
325exports.etag = deprecate.function(exports.etag,
326 'utils.etag: this private api moved with serve-static');
327
328/**
329 * Parse the given Cache-Control `str`.
330 *
331 * @param {String} str
332 * @return {Object}
333 * @api private
334 */
335
336exports.parseCacheControl = function(str){
337 var directives = str.split(',')
338 , obj = {};
339
340 for(var i = 0, len = directives.length; i < len; i++) {
341 var parts = directives[i].split('=')
342 , key = parts.shift().trim()
343 , val = parseInt(parts.shift(), 10);
344
345 obj[key] = isNaN(val) ? true : val;
346 }
347
348 return obj;
349};
350
351/**
352 * Parse the `req` url with memoization.
353 *
354 * @param {ServerRequest} req
355 * @return {Object}
356 * @api private
357 */
358
359exports.parseUrl = deprecate.function(parseurl,
360 'utils.parseUrl: use parseurl npm module instead');
361
362/**
363 * Parse byte `size` string.
364 *
365 * @param {String} size
366 * @return {Number}
367 * @api private
368 */
369
370exports.parseBytes = deprecate.function(bytes,
371 'utils.parseBytes: use bytes npm module instead');
372
373/**
374 * Normalizes the path separator from system separator
375 * to URL separator, aka `/`.
376 *
377 * @param {String} path
378 * @return {String}
379 * @api private
380 */
381
382exports.normalizeSlashes = function normalizeSlashes(path) {
383 return path.split(sep).join('/');
384};
385
386exports.normalizeSlashes = deprecate.function(exports.normalizeSlashes,
387 'utils.normalizeSlashes: this private api moved with serve-index');
388
389/**
390 * Set the charset in a given Content-Type string if none exists.
391 *
392 * @param {String} type
393 * @param {String} charset
394 * @return {String}
395 * @api private
396 */
397
398exports.setCharset = function setCharset(type, charset) {
399 if (!type || !charset) return type;
400
401 var parsed = contentType.parse(type);
402 var exists = parsed.parameters.charset;
403
404 // keep existing charset
405 if (exists) {
406 return type;
407 }
408
409 // set charset
410 parsed.parameters.charset = charset;
411
412 return contentType.format(parsed);
413};
414
415function noop() {}