diff --git a/lib/url.js b/lib/url.js index c23d9c51c20033..d7c274a96924ac 100644 --- a/lib/url.js +++ b/lib/url.js @@ -27,6 +27,9 @@ const { ObjectAssign, ObjectKeys, StringPrototypeCharCodeAt, + StringPrototypeIndexOf, + StringPrototypeReplaceAll, + StringPrototypeSlice, decodeURIComponent, } = primordials; @@ -637,6 +640,10 @@ Url.prototype.format = function format() { } let protocol = this.protocol || ''; + if (protocol && StringPrototypeCharCodeAt(protocol, protocol.length - 1) !== 58 /* : */) { + protocol += ':'; + } + let pathname = this.pathname || ''; let hash = this.hash || ''; let host = ''; @@ -646,7 +653,7 @@ Url.prototype.format = function format() { host = auth + this.host; } else if (this.hostname) { host = auth + ( - this.hostname.includes(':') && !isIpv6Hostname(this.hostname) ? + StringPrototypeIndexOf(this.hostname, ':') !== -1 && !isIpv6Hostname(this.hostname) ? '[' + this.hostname + ']' : this.hostname ); @@ -658,59 +665,55 @@ Url.prototype.format = function format() { if (this.query !== null && typeof this.query === 'object') { query = querystring.stringify(this.query); } - let search = this.search || (query && ('?' + query)) || ''; - if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58/* : */) - protocol += ':'; - - let newPathname = ''; - let lastPos = 0; - for (let i = 0; i < pathname.length; ++i) { - switch (pathname.charCodeAt(i)) { - case CHAR_HASH: - if (i - lastPos > 0) - newPathname += pathname.slice(lastPos, i); - newPathname += '%23'; - lastPos = i + 1; - break; - case CHAR_QUESTION_MARK: - if (i - lastPos > 0) - newPathname += pathname.slice(lastPos, i); - newPathname += '%3F'; + if (StringPrototypeIndexOf(pathname, '#') !== -1 || StringPrototypeIndexOf(pathname, '?') !== -1) { + let newPathname = ''; + let lastPos = 0; + const len = pathname.length; + for (let i = 0; i < len; i++) { + const code = StringPrototypeCharCodeAt(pathname, i); + if (code === CHAR_HASH || code === CHAR_QUESTION_MARK) { + if (i > lastPos) { + newPathname += StringPrototypeSlice(pathname, lastPos, i); + } + newPathname += (code === CHAR_HASH ? '%23' : '%3F'); lastPos = i + 1; - break; + } } - } - if (lastPos > 0) { - if (lastPos !== pathname.length) - pathname = newPathname + pathname.slice(lastPos); - else - pathname = newPathname; + if (lastPos < len) { + newPathname += StringPrototypeSlice(pathname, lastPos); + } + pathname = newPathname; } // Only the slashedProtocols get the //. Not mailto:, xmpp:, etc. // unless they had them to begin with. if (this.slashes || slashedProtocol.has(protocol)) { if (this.slashes || host) { - if (pathname && pathname.charCodeAt(0) !== CHAR_FORWARD_SLASH) + if (pathname && StringPrototypeCharCodeAt(pathname, 0) !== CHAR_FORWARD_SLASH) pathname = '/' + pathname; host = '//' + host; } else if (protocol.length >= 4 && - protocol.charCodeAt(0) === 102/* f */ && - protocol.charCodeAt(1) === 105/* i */ && - protocol.charCodeAt(2) === 108/* l */ && - protocol.charCodeAt(3) === 101/* e */) { + StringPrototypeCharCodeAt(protocol, 0) === 102/* f */ && + StringPrototypeCharCodeAt(protocol, 1) === 105/* i */ && + StringPrototypeCharCodeAt(protocol, 2) === 108/* l */ && + StringPrototypeCharCodeAt(protocol, 3) === 101/* e */) { host = '//'; } } - search = search.replaceAll('#', '%23'); + // Escape '#' in search. + if (StringPrototypeIndexOf(search, '#') !== -1) { + search = StringPrototypeReplaceAll(search, '#', '%23'); + } - if (hash && hash.charCodeAt(0) !== CHAR_HASH) + if (hash && StringPrototypeCharCodeAt(hash, 0) !== CHAR_HASH) { hash = '#' + hash; - if (search && search.charCodeAt(0) !== CHAR_QUESTION_MARK) + } + if (search && StringPrototypeCharCodeAt(search, 0) !== CHAR_QUESTION_MARK) { search = '?' + search; + } return protocol + host + pathname + search + hash; };