diff --git a/lib/url.js b/lib/url.js index b8c1105a9f2..d497116f58a 100644 --- a/lib/url.js +++ b/lib/url.js @@ -24,7 +24,6 @@ var protocolPattern = /^([a-z0-9]+:)/, 'gopher:': true, 'file:': true }, - path = require('path'), // internal module, guaranteed to be loaded already. querystring = require('querystring'); function urlParse(url, parseQueryString, slashesDenoteHost) { @@ -280,7 +279,6 @@ function urlResolveObject(source, relative) { return source; } - // resolve dots. // if a url ENDs in . or .., then it must get a trailing slash. // however, if it ends in anything else non-slashy, // then it must NOT get a trailing slash. @@ -289,27 +287,34 @@ function urlResolveObject(source, relative) { (source.host || relative.host) && (last === '.' || last === '..') || last === ''); - // Figure out if this has to end up as an absolute url, - // or should continue to be relative. - srcPath = path.normalizeArray(srcPath, true); - if (srcPath.length === 1 && srcPath[0] === '.') srcPath = []; - if (mustEndAbs || removeAllDots) { - // all dots must go. - var dirs = []; - srcPath.forEach(function(dir, i) { - if (dir === '..') { - dirs.pop(); - } else if (dir !== '.') { - dirs.push(dir); - } - }); - - if (mustEndAbs && dirs[0] !== '' && - (!dirs[0] || dirs[0].charAt(0) !== '/')) { - dirs.unshift(''); + // strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last == '.') { + srcPath.splice(i, 1); + } else if (last === '..') { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; } - srcPath = dirs; } + + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for ( ; up--; up) { + srcPath.unshift('..'); + } + } + + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); + } + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { srcPath.push(''); }