diff options
Diffstat (limited to 'data/extensions/jid1-KtlZuoiikVfFew@jetpack/resources/librejs/lib/url_handler/node_url.js')
-rw-r--r-- | data/extensions/jid1-KtlZuoiikVfFew@jetpack/resources/librejs/lib/url_handler/node_url.js | 558 |
1 files changed, 319 insertions, 239 deletions
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/resources/librejs/lib/url_handler/node_url.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/resources/librejs/lib/url_handler/node_url.js index 6b4940f..71d31a6 100644 --- a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/resources/librejs/lib/url_handler/node_url.js +++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/resources/librejs/lib/url_handler/node_url.js @@ -26,28 +26,49 @@ exports.resolve = urlResolve; exports.resolveObject = urlResolveObject; exports.format = urlFormat; +exports.Url = Url; + +function Url() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.host = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.query = null; + this.pathname = null; + this.path = null; + this.href = null; +} + // Reference: RFC 3986, RFC 1808, RFC 2396 // define these here so at least they only have to be // compiled once on the first module load. var protocolPattern = /^([a-z0-9.+-]+:)/i, - portPattern = /:[0-9]+$/, + portPattern = /:[0-9]*$/, + // RFC 2396: characters reserved for delimiting URLs. + // We actually just auto-escape these. delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], + // RFC 2396: characters not allowed for various reasons. - unwise = ['{', '}', '|', '\\', '^', '~', '[', ']', '`'].concat(delims), + unwise = ['{', '}', '|', '\\', '^', '~', '`'].concat(delims), + // Allowed by RFCs, but cause of XSS attacks. Always escape these. - autoEscape = ['\''], + autoEscape = ['\''].concat(delims), // Characters that are never ever allowed in a hostname. // Note that any invalid chars are also handled, but these // are the ones that are *expected* to be seen, so we fast-path // them. nonHostChars = ['%', '/', '?', ';', '#'] .concat(unwise).concat(autoEscape), - nonAuthChars = ['/', '@', '?', '#'].concat(delims), + hostEndingChars = ['/', '?', '#'], hostnameMaxLen = 255, - hostnamePartPattern = /^[a-zA-Z0-9][a-z0-9A-Z_-]{0,62}$/, - hostnamePartStart = /^([a-zA-Z0-9][a-z0-9A-Z_-]{0,62})(.*)$/, + hostnamePartPattern = /^[a-z0-9A-Z_-]{0,63}$/, + hostnamePartStart = /^([a-z0-9A-Z_-]{0,63})(.*)$/, // protocols that can allow "unsafe" and "unwise" chars. unsafeProtocol = { 'javascript': true, @@ -58,18 +79,6 @@ var protocolPattern = /^([a-z0-9.+-]+:)/i, 'javascript': true, 'javascript:': true }, - // protocols that always have a path component. - pathedProtocol = { - 'http': true, - 'https': true, - 'ftp': true, - 'gopher': true, - 'file': true, - 'http:': true, - 'ftp:': true, - 'gopher:': true, - 'file:': true - }, // protocols that always contain a // bit. slashedProtocol = { 'http': true, @@ -86,28 +95,35 @@ var protocolPattern = /^([a-z0-9.+-]+:)/i, querystring = require('url_handler/node_querystring'); function urlParse(url, parseQueryString, slashesDenoteHost) { - if (url && typeof(url) === 'object' && url.href) return url; + if (url && typeof(url) === 'object' && url instanceof Url) return url; + + var u = new Url; + u.parse(url, parseQueryString, slashesDenoteHost); + return u; +} +Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { if (typeof url !== 'string') { throw new TypeError("Parameter 'url' must be a string, not " + typeof url); } - var out = {}, - rest = url; - out.href = ""; - // cut off any delimiters. - // This is to support parse stuff like "<http://foo.com>" - for (var i = 0, l = rest.length; i < l; i++) { - if (delims.indexOf(rest.charAt(i)) === -1) break; - } - if (i !== 0) rest = rest.substr(i); + // Copy chrome, IE, opera backslash-handling behavior. + // See: https://code.google.com/p/chromium/issues/detail?id=25916 + var hashSplit = url.split('#'); + hashSplit[0] = hashSplit[0].replace(/\\/g, '/'); + url = hashSplit.join('#'); + var rest = url; + + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); var proto = protocolPattern.exec(rest); if (proto) { proto = proto[0]; var lowerProto = proto.toLowerCase(); - out.protocol = lowerProto; + this.protocol = lowerProto; rest = rest.substr(proto.length); } @@ -119,71 +135,85 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { var slashes = rest.substr(0, 2) === '//'; if (slashes && !(proto && hostlessProtocol[proto])) { rest = rest.substr(2); - out.slashes = true; + this.slashes = true; } } if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) { + // there's a hostname. // the first instance of /, ?, ;, or # ends the host. - // don't enforce full RFC correctness, just be unstupid about it. - + // // If there is an @ in the hostname, then non-host chars *are* allowed - // to the left of the first @ sign, unless some non-auth character + // to the left of the last @ sign, unless some host-ending character // comes *before* the @-sign. // URLs are obnoxious. - var atSign = rest.indexOf('@'); - if (atSign !== -1) { - // there *may be* an auth - var hasAuth = true; - for (var i = 0, l = nonAuthChars.length; i < l; i++) { - var index = rest.indexOf(nonAuthChars[i]); - if (index !== -1 && index < atSign) { - // not a valid auth. Something like http://foo.com/bar@baz/ - hasAuth = false; - break; - } - } - if (hasAuth) { - // pluck off the auth portion. - out.auth = rest.substr(0, atSign); - rest = rest.substr(atSign + 1); - } + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:c path:/?@c + + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. + + // find the first instance of any hostEndingChars + var hostEnd = -1; + for (var i = 0; i < hostEndingChars.length; i++) { + var hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; } - var firstNonHost = -1; - for (var i = 0, l = nonHostChars.length; i < l; i++) { - var index = rest.indexOf(nonHostChars[i]); - if (index !== -1 && - (firstNonHost < 0 || index < firstNonHost)) firstNonHost = index; + // at this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + var auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf('@'); + } else { + // atSign must be in auth portion. + // http://a@b/c@d => host:b auth:a path:/c@d + atSign = rest.lastIndexOf('@', hostEnd); } - if (firstNonHost !== -1) { - out.host = rest.substr(0, firstNonHost); - rest = rest.substr(firstNonHost); - } else { - out.host = rest; - rest = ''; + // Now we have a portion which is definitely the auth. + // Pull that off. + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = decodeURIComponent(auth); } - // pull out port. - var p = parseHost(out.host); - var keys = Object.keys(p); - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - out[key] = p[key]; + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (var i = 0; i < nonHostChars.length; i++) { + var hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) + hostEnd = rest.length; + + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); + + // pull out port. + this.parseHost(); // we've indicated that there is a hostname, // so even if it's empty, it has to be present. - out.hostname = out.hostname || ''; + this.hostname = this.hostname || ''; + + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = this.hostname[0] === '[' && + this.hostname[this.hostname.length - 1] === ']'; // validate a little. - if (out.hostname.length > hostnameMaxLen) { - out.hostname = ''; - } else { - var hostparts = out.hostname.split(/\./); + if (!ipv6Hostname) { + var hostparts = this.hostname.split(/\./); for (var i = 0, l = hostparts.length; i < l; i++) { var part = hostparts[i]; if (!part) continue; @@ -211,32 +241,41 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { if (notHost.length) { rest = '/' + notHost.join('.') + rest; } - out.hostname = validParts.join('.'); + this.hostname = validParts.join('.'); break; } } } } - // hostnames are always lower case. - out.hostname = out.hostname.toLowerCase(); - - // IDNA Support: Returns a puny coded representation of "domain". - // It only converts the part of the domain name that - // has non ASCII characters. I.e. it dosent matter if - // you call it with a domain that already is in ASCII. - var domainArray = out.hostname.split('.'); - var newOut = []; - for (var i = 0; i < domainArray.length; ++i) { - var s = domainArray[i]; - newOut.push(s.match(/[^A-Za-z0-9_-]/) ? - 'xn--' + punycode.encode(s) : s); + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); + } + + if (!ipv6Hostname) { + // IDNA Support: Returns a punycoded representation of "domain". + // It only converts parts of the domain name that + // have non-ASCII characters, i.e. it doesn't matter if + // you call it with a domain that already is ASCII-only. + this.hostname = punycode.toASCII(this.hostname); } - out.hostname = newOut.join('.'); - out.host = (out.hostname || '') + - ((out.port) ? ':' + out.port : ''); - out.href += out.host; + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; + this.host = h + p; + this.href += this.host; + + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; + } + } } // now rest is set to the post-host stuff. @@ -254,16 +293,6 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { } rest = rest.split(ae).join(esc); } - - // Now make sure that delims never appear in a url. - var chop = rest.length; - for (var i = 0, l = delims.length; i < l; i++) { - var c = rest.indexOf(delims[i]); - if (c !== -1) { - chop = Math.min(c, chop); - } - } - rest = rest.substr(0, chop); } @@ -271,38 +300,39 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { var hash = rest.indexOf('#'); if (hash !== -1) { // got a fragment string. - out.hash = rest.substr(hash); + this.hash = rest.substr(hash); rest = rest.slice(0, hash); } var qm = rest.indexOf('?'); if (qm !== -1) { - out.search = rest.substr(qm); - out.query = rest.substr(qm + 1); + this.search = rest.substr(qm); + this.query = rest.substr(qm + 1); if (parseQueryString) { - out.query = querystring.parse(out.query); + this.query = querystring.parse(this.query); } rest = rest.slice(0, qm); } else if (parseQueryString) { // no query string, but parseQueryString still requested - out.search = ''; - out.query = {}; + this.search = ''; + this.query = {}; } - if (rest) out.pathname = rest; - if (slashedProtocol[proto] && - out.hostname && !out.pathname) { - out.pathname = '/'; + if (rest) this.pathname = rest; + if (slashedProtocol[lowerProto] && + this.hostname && !this.pathname) { + this.pathname = '/'; } //to support http.request - if (out.pathname || out.search) { - out.path = (out.pathname ? out.pathname : '') + - (out.search ? out.search : ''); + if (this.pathname || this.search) { + var p = this.pathname || ''; + var s = this.search || ''; + this.path = p + s; } // finally, reconstruct the href based on what has been validated. - out.href = urlFormat(out); - return out; -} + this.href = this.format(); + return this; +}; // format a parsed object into a url string function urlFormat(obj) { @@ -311,38 +341,47 @@ function urlFormat(obj) { // this way, you can call url_format() on strings // to clean up potentially wonky urls. if (typeof(obj) === 'string') obj = urlParse(obj); + if (!(obj instanceof Url)) return Url.prototype.format.call(obj); + return obj.format(); +} - var auth = obj.auth || ''; +Url.prototype.format = function() { + var auth = this.auth || ''; if (auth) { - auth = auth.split('@').join('%40'); - for (var i = 0, l = nonAuthChars.length; i < l; i++) { - var nAC = nonAuthChars[i]; - auth = auth.split(nAC).join(encodeURIComponent(nAC)); - } + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); auth += '@'; } - var protocol = obj.protocol || '', - host = (obj.host !== undefined) ? auth + obj.host : - obj.hostname !== undefined ? ( - auth + obj.hostname + - (obj.port ? ':' + obj.port : '') - ) : - false, - pathname = obj.pathname || '', - query = obj.query && - ((typeof obj.query === 'object' && - Object.keys(obj.query).length) ? - querystring.stringify(obj.query) : - '') || '', - search = obj.search || (query && ('?' + query)) || '', - hash = obj.hash || ''; + var protocol = this.protocol || '', + pathname = this.pathname || '', + hash = this.hash || '', + host = false, + query = ''; + + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + (this.hostname.indexOf(':') === -1 ? + this.hostname : + '[' + this.hostname + ']'); + if (this.port) { + host += ':' + this.port; + } + } + + if (this.query && typeof this.query === 'object' && + Object.keys(this.query).length) { + query = querystring.stringify(this.query); + } + + var search = this.search || (query && ('?' + query)) || ''; if (protocol && protocol.substr(-1) !== ':') protocol += ':'; // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. // unless they had them to begin with. - if (obj.slashes || + if (this.slashes || (!protocol || slashedProtocol[protocol]) && host !== false) { host = '//' + (host || ''); if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; @@ -353,40 +392,68 @@ function urlFormat(obj) { if (hash && hash.charAt(0) !== '#') hash = '#' + hash; if (search && search.charAt(0) !== '?') search = '?' + search; + pathname = pathname.replace(/[?#]/g, function(match) { + return encodeURIComponent(match); + }); + search = search.replace('#', '%23'); + return protocol + host + pathname + search + hash; -} +}; function urlResolve(source, relative) { - return urlFormat(urlResolveObject(source, relative)); + return urlParse(source, false, true).resolve(relative); } +Url.prototype.resolve = function(relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); +}; + function urlResolveObject(source, relative) { if (!source) return relative; + return urlParse(source, false, true).resolveObject(relative); +} + +Url.prototype.resolveObject = function(relative) { + if (typeof relative === 'string') { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; + } - source = urlParse(urlFormat(source), false, true); - relative = urlParse(urlFormat(relative), false, true); + var result = new Url(); + Object.keys(this).forEach(function(k) { + result[k] = this[k]; + }, this); // hash is always overridden, no matter what. - source.hash = relative.hash; + // even href="" will remove it. + result.hash = relative.hash; + // if the relative url is empty, then there's nothing left to do here. if (relative.href === '') { - source.href = urlFormat(source); - return source; + result.href = result.format(); + return result; } // hrefs like //foo/bar always cut to the protocol. if (relative.slashes && !relative.protocol) { - relative.protocol = source.protocol; + // take everything except the protocol from relative + Object.keys(relative).forEach(function(k) { + if (k !== 'protocol') + result[k] = relative[k]; + }); + //urlParse appends trailing / to urls like http://www.example.com - if (slashedProtocol[relative.protocol] && - relative.hostname && !relative.pathname) { - relative.path = relative.pathname = '/'; + if (slashedProtocol[result.protocol] && + result.hostname && !result.pathname) { + result.path = result.pathname = '/'; } - relative.href = urlFormat(relative); - return relative; + + result.href = result.format(); + return result; } - if (relative.protocol && relative.protocol !== source.protocol) { + if (relative.protocol && relative.protocol !== result.protocol) { // if it's a known url protocol, then changing // the protocol does weird things // first, if it's not file:, then we MUST have a host, @@ -396,10 +463,14 @@ function urlResolveObject(source, relative) { // because that's known to be hostless. // anything else is assumed to be absolute. if (!slashedProtocol[relative.protocol]) { - relative.href = urlFormat(relative); - return relative; + Object.keys(relative).forEach(function(k) { + result[k] = relative[k]; + }); + result.href = result.format(); + return result; } - source.protocol = relative.protocol; + + result.protocol = relative.protocol; if (!relative.host && !hostlessProtocol[relative.protocol]) { var relPath = (relative.pathname || '').split('/'); while (relPath.length && !(relative.host = relPath.shift())); @@ -407,72 +478,72 @@ function urlResolveObject(source, relative) { if (!relative.hostname) relative.hostname = ''; if (relPath[0] !== '') relPath.unshift(''); if (relPath.length < 2) relPath.unshift(''); - relative.pathname = relPath.join('/'); + result.pathname = relPath.join('/'); + } else { + result.pathname = relative.pathname; } - source.pathname = relative.pathname; - source.search = relative.search; - source.query = relative.query; - source.host = relative.host || ''; - source.auth = relative.auth; - source.hostname = relative.hostname || relative.host; - source.port = relative.port; - //to support http.request - if (source.pathname !== undefined || source.search !== undefined) { - source.path = (source.pathname ? source.pathname : '') + - (source.search ? source.search : ''); + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ''; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // to support http.request + if (result.pathname || result.search) { + var p = result.pathname || ''; + var s = result.search || ''; + result.path = p + s; } - source.slashes = source.slashes || relative.slashes; - source.href = urlFormat(source); - return source; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; } - var isSourceAbs = (source.pathname && source.pathname.charAt(0) === '/'), + var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'), isRelAbs = ( - relative.host !== undefined || + relative.host || relative.pathname && relative.pathname.charAt(0) === '/' ), mustEndAbs = (isRelAbs || isSourceAbs || - (source.host && relative.pathname)), + (result.host && relative.pathname)), removeAllDots = mustEndAbs, - srcPath = source.pathname && source.pathname.split('/') || [], + srcPath = result.pathname && result.pathname.split('/') || [], relPath = relative.pathname && relative.pathname.split('/') || [], - psychotic = source.protocol && - !slashedProtocol[source.protocol]; + psychotic = result.protocol && !slashedProtocol[result.protocol]; // if the url is a non-slashed url, then relative // links like ../.. should be able // to crawl up to the hostname, as well. This is strange. - // source.protocol has already been set by now. + // result.protocol has already been set by now. // Later on, put the first path part into the host field. if (psychotic) { - - delete source.hostname; - delete source.port; - if (source.host) { - if (srcPath[0] === '') srcPath[0] = source.host; - else srcPath.unshift(source.host); + result.hostname = ''; + result.port = null; + if (result.host) { + if (srcPath[0] === '') srcPath[0] = result.host; + else srcPath.unshift(result.host); } - delete source.host; + result.host = ''; if (relative.protocol) { - delete relative.hostname; - delete relative.port; + relative.hostname = null; + relative.port = null; if (relative.host) { if (relPath[0] === '') relPath[0] = relative.host; else relPath.unshift(relative.host); } - delete relative.host; + relative.host = null; } mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); } if (isRelAbs) { // it's absolute. - source.host = (relative.host || relative.host === '') ? - relative.host : source.host; - source.hostname = (relative.hostname || relative.hostname === '') ? - relative.hostname : source.hostname; - source.search = relative.search; - source.query = relative.query; + result.host = (relative.host || relative.host === '') ? + relative.host : result.host; + result.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : result.hostname; + result.search = relative.search; + result.query = relative.query; srcPath = relPath; // fall through to the dot-handling below. } else if (relPath.length) { @@ -481,53 +552,55 @@ function urlResolveObject(source, relative) { if (!srcPath) srcPath = []; srcPath.pop(); srcPath = srcPath.concat(relPath); - source.search = relative.search; - source.query = relative.query; - } else if ('search' in relative) { + result.search = relative.search; + result.query = relative.query; + } else if (relative.search !== null && relative.search !== undefined) { // just pull out the search. // like href='?foo'. // Put this after the other two cases because it simplifies the booleans if (psychotic) { - source.hostname = source.host = srcPath.shift(); + result.hostname = result.host = srcPath.shift(); //occationaly the auth can get stuck only in host //this especialy happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') - var authInHost = source.host && source.host.indexOf('@') > 0 ? - source.host.split('@') : false; + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; if (authInHost) { - source.auth = authInHost.shift(); - source.host = source.hostname = authInHost.shift(); + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); } } - source.search = relative.search; - source.query = relative.query; + result.search = relative.search; + result.query = relative.query; //to support http.request - if (source.pathname !== undefined || source.search !== undefined) { - source.path = (source.pathname ? source.pathname : '') + - (source.search ? source.search : ''); + if (result.pathname !== null || result.search !== null) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); } - source.href = urlFormat(source); - return source; + result.href = result.format(); + return result; } + if (!srcPath.length) { // no path at all. easy. // we've already handled the other stuff above. - delete source.pathname; + result.pathname = null; //to support http.request - if (!source.search) { - source.path = '/' + source.search; + if (result.search) { + result.path = '/' + result.search; } else { - delete source.path; + result.path = null; } - source.href = urlFormat(source); - return source; + result.href = result.format(); + return result; } + // 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. var last = srcPath.slice(-1)[0]; var hasTrailingSlash = ( - (source.host || relative.host) && (last === '.' || last === '..') || + (result.host || relative.host) && (last === '.' || last === '..') || last === ''); // strip single dots, resolve double dots to parent dir @@ -563,49 +636,56 @@ function urlResolveObject(source, relative) { } var isAbsolute = srcPath[0] === '' || - (srcPath[0] && srcPath[0].charAt(0) === '/'); + (srcPath[0] && srcPath[0].charAt(0) === '/'); // put the host back if (psychotic) { - source.hostname = source.host = isAbsolute ? '' : - srcPath.length ? srcPath.shift() : ''; + result.hostname = result.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; //occationaly the auth can get stuck only in host //this especialy happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') - var authInHost = source.host && source.host.indexOf('@') > 0 ? - source.host.split('@') : false; + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; if (authInHost) { - source.auth = authInHost.shift(); - source.host = source.hostname = authInHost.shift(); + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); } } - mustEndAbs = mustEndAbs || (source.host && srcPath.length); + mustEndAbs = mustEndAbs || (result.host && srcPath.length); if (mustEndAbs && !isAbsolute) { srcPath.unshift(''); } - source.pathname = srcPath.join('/'); - //to support request.http - if (source.pathname !== undefined || source.search !== undefined) { - source.path = (source.pathname ? source.pathname : '') + - (source.search ? source.search : ''); + if (!srcPath.length) { + result.pathname = null; + result.path = null; + } else { + result.pathname = srcPath.join('/'); } - source.auth = relative.auth || source.auth; - source.slashes = source.slashes || relative.slashes; - source.href = urlFormat(source); - return source; -} -function parseHost(host) { - var out = {}; + //to support request.http + if (result.pathname !== null || result.search !== null) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; +}; + +Url.prototype.parseHost = function() { + var host = this.host; var port = portPattern.exec(host); if (port) { port = port[0]; - out.port = port.substr(1); + if (port !== ':') { + this.port = port.substr(1); + } host = host.substr(0, host.length - port.length); } - if (host) out.hostname = host; - return out; -}
\ No newline at end of file + if (host) this.hostname = host; +}; |