diff --git a/lib/_http_client.js b/lib/_http_client.js index cdf0726e78c9e0..371da638f9b98a 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -81,6 +81,17 @@ function ClientRequest(options, cb) { } if (host && !this.getHeader('host') && setHost) { var hostHeader = host; + var posColon = -1; + + // For the Host header, ensure that IPv6 addresses are enclosed + // in square brackets, as defined by URI formatting + // https://tools.ietf.org/html/rfc3986#section-3.2.2 + if (-1 !== (posColon = hostHeader.indexOf(':')) && + -1 !== (posColon = hostHeader.indexOf(':', posColon)) && + '[' !== hostHeader[0]) { + hostHeader = `[${hostHeader}]`; + } + if (port && +port !== defaultPort) { hostHeader += ':' + port; } diff --git a/test/parallel/test-http-host-header-ipv6-fail.js b/test/parallel/test-http-host-header-ipv6-fail.js new file mode 100644 index 00000000000000..8a1a9a61afe5af --- /dev/null +++ b/test/parallel/test-http-host-header-ipv6-fail.js @@ -0,0 +1,40 @@ +'use strict'; +/* + * When using the object form of http.request and using an IPv6 address + * as a hostname, and using a non-standard port, the Host header + * is improperly formatted. + * Issue: https://github.com/nodejs/node/issues/5308 + * As per https://tools.ietf.org/html/rfc7230#section-5.4 and + * https://tools.ietf.org/html/rfc3986#section-3.2.2 + * the IPv6 address should be enclosed in square brackets + */ + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const hostname = '::1'; +const port = common.PORT; + +function httpreq() { + var req = http.request({ + host: hostname, + port: port, + path: '/', + method: 'GET' + }); + req.end(); +} + +if (!common.hasIPv6) { + console.error('Skipping test, no IPv6 support'); + return; +} + +const server = http.createServer(common.mustCall(function(req, res) { + assert.ok(req.headers.host, `[${hostname}]`); + res.end(); + server.close(true); +})); + +server.listen(port, hostname, () => httpreq());