diff --git a/lib/url.js b/lib/url.js index cc36216f9eb916..49f99a8b5fa621 100644 --- a/lib/url.js +++ b/lib/url.js @@ -26,6 +26,7 @@ const { ObjectCreate, ObjectKeys, SafeSet, + StringPrototypeCharCodeAt, } = primordials; const { toASCII } = require('internal/idna'); @@ -156,6 +157,14 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { return urlObject; } +function isIpv6Hostname(hostname) { + return ( + StringPrototypeCharCodeAt(hostname, 0) === CHAR_LEFT_SQUARE_BRACKET && + StringPrototypeCharCodeAt(hostname, hostname.length - 1) === + CHAR_RIGHT_SQUARE_BRACKET + ); +} + Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { validateString(url, 'url'); @@ -364,8 +373,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { // If hostname begins with [ and ends with ] // assume that it's an IPv6 address. - const ipv6Hostname = hostname.charCodeAt(0) === CHAR_LEFT_SQUARE_BRACKET && - hostname.charCodeAt(hostname.length - 1) === CHAR_RIGHT_SQUARE_BRACKET; + const ipv6Hostname = isIpv6Hostname(hostname); // validate a little. if (!ipv6Hostname) { @@ -590,7 +598,7 @@ Url.prototype.format = function format() { host = auth + this.host; } else if (this.hostname) { host = auth + ( - this.hostname.includes(':') ? + this.hostname.includes(':') && !isIpv6Hostname(this.hostname) ? '[' + this.hostname + ']' : this.hostname ); diff --git a/test/parallel/test-url-format.js b/test/parallel/test-url-format.js index 2d0132eb38a0bb..720a10a97a979b 100644 --- a/test/parallel/test-url-format.js +++ b/test/parallel/test-url-format.js @@ -148,6 +148,12 @@ const formatTests = { host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', pathname: '/s/stopButton' }, + 'http://[::]/': { + href: 'http://[::]/', + protocol: 'http:', + hostname: '[::]', + pathname: '/' + }, // Encode context-specific delimiters in path and query, but do not touch // other non-delimiter chars like `%`.