-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
Description
Version
v17.0.0
Platform
Darwin pink.local 20.6.0 Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 x86_64
Subsystem
No response
What steps will reproduce the bug?
var http = require('http')
var server = http.createServer(function (req, res) { /* ... */ })
server.listen(3000, 'localhost', function (_err) {
console.log('server listening: %s', server.address())
})I'm not sure what the underlying mechanism is, but it looks to me like the resolution of "localhost" for server.listen(PORT, HOST, ...) and for http.request('http://localhost/...', ...) changed from favouring IPv4 in node v16 and earlier, to favouring IPv6. The result is some possibly confusing breakages when using IPv4 values such as 127.0.0.1 and 0.0.0.0. For example, the following script errors with node v17, but succeeds with node v16:
// example-localhost-means-which-ipv.js
var http = require('http')
var server = http.createServer(function (req, res) {
req.on('data', function (chunk) {
console.log('server req data: %s', chunk)
})
req.on('end', function () {
console.log('server req end')
res.end('pong')
})
})
// Listen on IPv4 address.
var theHost = '127.0.0.1'
server.listen(3000, theHost, function (_err) {
console.log('server listening: %s', server.address())
// GET from localhost.
var theUrl = 'http://localhost:3000/ping'
console.log('client req: GET %s', theUrl)
http.get(theUrl, function (res) {
console.log('client res:', res.statusCode, res.headers)
res.on('data', (chunk) => {
console.log('client data: %s', chunk)
})
res.on('end', () => {
console.log('client end')
server.close()
})
})
})% node --version
v17.0.0
% node example-localhost-means-which-ipv.js
server listening: { address: '127.0.0.1', family: 'IPv4', port: 3000 }
client req: GET http://localhost:3000/ping
node:events:368
throw er; // Unhandled 'error' event
^
Error: connect ECONNREFUSED ::1:3000
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1161:16)
Emitted 'error' event on ClientRequest instance at:
at Socket.socketErrorListener (node:_http_client:447:9)
at Socket.emit (node:events:390:28)
at emitErrorNT (node:internal/streams/destroy:164:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
errno: -61,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '::1',
port: 3000
}
Node.js v17.0.0
% nvm use 16
Now using node v16.11.1 (npm v8.0.0)
% node example-localhost-means-which-ipv.js
server listening: { address: '127.0.0.1', family: 'IPv4', port: 3000 }
client req: GET http://localhost:3000/ping
server req end
client res: 200 {
date: 'Wed, 20 Oct 2021 21:01:56 GMT',
connection: 'close',
'content-length': '4'
}
client data: pong
client end
Similarly, if we reverse things to listen on 'localhost' and GET from 'http://[::1]:3000/ping' (IPv6), then it fails on node v16 and passes on node v17.
As I said above, I don't know the mechanism for selecting IPv4 or IPv6. I suspect this isn't a "bug", except perhaps a lack of documentation? I don't see any mention of localhost of IPv6 in the changelog. Thanks.
How often does it reproduce? Is there a required condition?
Everytime. While I tested mostly on macOS, I believe I was seeing this in GitHub Action tests running on linux containers.
What is the expected behavior?
No response
What do you see instead?
No response
Additional information
No response