4 * Copyright(c) 2010 Sencha Inc.
5 * Copyright(c) 2011 TJ Holowaychuk
6 * Copyright(c) 2014-2015 Douglas Christopher Wilson
11 * Module dependencies.
14 var bytes = require('bytes');
15 var contentType = require('content-type');
16 var cookieParser = require('cookie-parser');
17 var createError = require('http-errors');
18 var deprecate = require('depd')('connect');
19 var 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('.');
26 var merge = require('utils-merge');
29 * pause is broken in node < 0.10
31 exports.brokenPause = parseInt(nodeVersion[0], 10) === 0
32 && parseInt(nodeVersion[1], 10) < 10;
35 * Return `true` if the request has a body, otherwise return `false`.
37 * @param {IncomingMessage} req
42 exports.hasBody = deprecate.function(typeis.hasBody,
43 'utils.hasBody: use type-is npm module instead');
46 * Extract the mime type from the given request's
47 * _Content-Type_ header.
49 * @param {IncomingMessage} req
54 exports.mime = function(req) {
55 var str = req.headers['content-type'] || ''
56 , i = str.indexOf(';');
57 return ~i ? str.slice(0, i) : str;
60 exports.mime = deprecate.function(exports.mime,
61 'utils.mime: use type-is npm module instead for mime comparisons');
64 * Generate an `Error` from the given status `code`
67 * @param {Number} code
73 exports.error = function(code, msg){
74 var err = new Error(msg || http.STATUS_CODES[code]);
79 exports.error = deprecate.function(exports.error,
80 'utils.error: use http-errors npm module instead');
83 * Return md5 hash of the given string and optional encoding,
87 * // => "e493298061761236c96b02ea6aa8a2ad"
90 * @param {String} encoding
95 exports.md5 = function(str, encoding){
99 .digest(encoding || 'hex');
102 exports.md5 = deprecate.function(exports.md5,
103 'utils.md5: use crypto npm module instead for hashing');
106 * Merge object b with object a.
108 * var a = { foo: 'bar' }
109 * , b = { bar: 'baz' };
112 * // => { foo: 'bar', bar: 'baz' }
120 exports.merge = deprecate.function(merge,
121 'utils.merge: use utils-merge npm module instead');
124 * Escape the given string of `html`.
126 * @param {String} html
131 exports.escape = function(html){
133 .replace(/&(?!\w+;)/g, '&')
134 .replace(/</g, '<')
135 .replace(/>/g, '>')
136 .replace(/"/g, '"');
139 exports.escape = deprecate.function(exports.escape,
140 'utils.escape: use escape-html npm module instead');
143 * Sign the given `val` with `secret`.
145 * @param {String} val
146 * @param {String} secret
151 exports.sign = deprecate.function(signature.sign,
152 'utils.sign: use cookie-signature npm module instead');
155 * Unsign and decode the given `val` with `secret`,
156 * returning `false` if the signature is invalid.
158 * @param {String} val
159 * @param {String} secret
160 * @return {String|Boolean}
164 exports.unsign = deprecate.function(signature.unsign,
165 'utils.unsign: use cookie-signature npm module instead');
168 * Parse signed cookies, returning an object
169 * containing the decoded key/value pairs,
170 * while removing the signed key from `obj`.
172 * @param {Object} obj
177 exports.parseSignedCookies = deprecate.function(cookieParser.signedCookies,
178 'utils.parseSignedCookies: use cookie-parser npm module instead');
181 * Parse a signed cookie string, return the decoded value
183 * @param {String} str signed cookie string
184 * @param {String} secret
185 * @return {String} decoded value
189 exports.parseSignedCookie = deprecate.function(cookieParser.signedCookie,
190 'utils.parseSignedCookie: use cookie-parser npm module instead');
193 * Parse JSON cookies.
195 * @param {Object} obj
200 exports.parseJSONCookies = deprecate.function(cookieParser.JSONCookies,
201 'utils.parseJSONCookies: use cookie-parser npm module instead');
204 * Parse JSON cookie string
206 * @param {String} str
207 * @return {Object} Parsed object or null if not json cookie
211 exports.parseJSONCookie = deprecate.function(cookieParser.JSONCookie,
212 'utils.parseJSONCookie: use cookie-parser npm module instead');
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
223 * var pause = utils.pause(req);
224 * fs.readFile(path, function(){
229 * @param {Object} obj
234 exports.pause = exports.brokenPause
244 * Strip `Content-*` headers from `res`.
246 * @param {ServerResponse} res
250 exports.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);
259 exports.removeContentHeaders = deprecate.function(exports.removeContentHeaders,
260 'utils.removeContentHeaders: this private api moved with serve-static');
263 * Check if `req` is a conditional GET request.
265 * @param {IncomingMessage} req
270 exports.conditionalGET = function(req) {
271 return req.headers['if-modified-since']
272 || req.headers['if-none-match'];
275 exports.conditionalGET = deprecate.function(exports.conditionalGET,
276 'utils.conditionalGET: use fresh npm module instead');
279 * Respond with 401 "Unauthorized".
281 * @param {ServerResponse} res
282 * @param {String} realm
286 exports.unauthorized = function(res, realm) {
287 res.statusCode = 401;
288 res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
289 res.end('Unauthorized');
292 exports.unauthorized = deprecate.function(exports.unauthorized,
293 'utils.unauthorized: this private api moved with basic-auth-connect');
296 * Respond with 304 "Not Modified".
298 * @param {ServerResponse} res
299 * @param {Object} headers
303 exports.notModified = function(res) {
304 exports.removeContentHeaders(res);
305 res.statusCode = 304;
309 exports.notModified = deprecate.function(exports.notModified,
310 'utils.notModified: this private api moved with serve-static');
313 * Return an ETag in the form of `"<size>-<mtime>"`
314 * from the given `stat`.
316 * @param {Object} stat
321 exports.etag = function(stat) {
322 return '"' + stat.size + '-' + Number(stat.mtime) + '"';
325 exports.etag = deprecate.function(exports.etag,
326 'utils.etag: this private api moved with serve-static');
329 * Parse the given Cache-Control `str`.
331 * @param {String} str
336 exports.parseCacheControl = function(str){
337 var directives = str.split(',')
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);
345 obj[key] = isNaN(val) ? true : val;
352 * Parse the `req` url with memoization.
354 * @param {ServerRequest} req
359 exports.parseUrl = deprecate.function(parseurl,
360 'utils.parseUrl: use parseurl npm module instead');
363 * Parse byte `size` string.
365 * @param {String} size
370 exports.parseBytes = deprecate.function(bytes,
371 'utils.parseBytes: use bytes npm module instead');
374 * Normalizes the path separator from system separator
375 * to URL separator, aka `/`.
377 * @param {String} path
382 exports.normalizeSlashes = function normalizeSlashes(path) {
383 return path.split(sep).join('/');
386 exports.normalizeSlashes = deprecate.function(exports.normalizeSlashes,
387 'utils.normalizeSlashes: this private api moved with serve-index');
390 * Set the charset in a given Content-Type string if none exists.
392 * @param {String} type
393 * @param {String} charset
398 exports.setCharset = function setCharset(type, charset) {
399 if (!type || !charset) return type;
401 var parsed = contentType.parse(type);
402 var exists = parsed.parameters.charset;
404 // keep existing charset
410 parsed.parameters.charset = charset;
412 return contentType.format(parsed);