Under node.js 16.13.1 (Windows), code like this worked (assume a server exists and works fine):
import net from 'net';
let socket = net.createConnection({
host: 'localhost',
port: 12345
})
After updating to node.js 17.7.2, the above now throws ECONNREFUSED (connection refused) errors. However, it works fine with '0.0.0.0' instead of 'localhost'.
In fact, even the documented default of "localhost" (thanks VLAZ) fails on 17.7.2:
// connects with 16.13.1, ECONNREFUSED with 17.7.2:
net.createConnection({port:12345});
// connects with both versions:
net.createConnection({port:12345,host:'0.0.0.0'});
// connects with 16.13.1, ECONNREFUSED with 17.7.2:
net.createConnection({port:12345,host:'localhost'});
I've confirmed that the behavior depends on the version of node. I can reliably avoid / reproduce the behavior at will by switching node versions, using any code that establishes a network connection.
I have a fair amount of code with hosts that default to "localhost". I'd rather not update all of that to "0.0.0.0" if I don't have to. More tedious is that often the user provides the host address and expects "localhost" to work, so now I have to add logic that converts "localhost" to "0.0.0.0" every time I create a socket with a user-specified host address. I mean, I'll do what I have to do, but this kind of stinks.
My question is: What happened between 16.13.1 and 17.7.2 that made "localhost" not useable any more? Is it a bug, or some intentional change? And, is there a way to make it work again or do I have to find+replace "localhost" with "0.0.0.0" everywhere now?
I suspect a lot of the "connection refused to localhost" errors that people ask about on the internet are related to whatever changed...
Update: It seems to have something to do with changes in how interfaces are enumerated (or something like that) in 17, rather than the specific use of "localhost".
For example, with the following test setup:
Then with 16.13.1:
| Listening on default | Listening on 0.0.0.0 | Listening on localhost | |
|---|---|---|---|
| Connect to default | OK | OK | OK |
| Connect to 0.0.0.0 | OK | OK | OK |
| Connect to localhost | OK | OK | OK |
But with 17.7.2:
| Listening on default | Listening on 0.0.0.0 | Listening on localhost | |
|---|---|---|---|
| Connect to default | OK | ECONNREFUSED | OK |
| Connect to 0.0.0.0 | OK | OK | ECONNREFUSED |
| Connect to localhost | OK | ECONNREFUSED | OK |
Now that's with servers created with node; also same node version for servers and clients. In my original case, my server was created with C++ and the standard socket() API, bound to INADDR_ANY (0.0.0.0).
Test code:
import net from 'net';
console.log(process.version);
const accepted = detail => socket => socket.write(detail, ()=>socket.end());
const serversReady = () => [
new Promise(resolve => net.createServer(accepted('default')).listen(12345, function(){resolve(this)})),
new Promise(resolve => net.createServer(accepted('localhost')).listen(12346, 'localhost', function(){resolve(this)})),
new Promise(resolve => net.createServer(accepted('0.0.0.0')).listen(12347, '0.0.0.0', function(){resolve(this)}))
];
const ports = [[12345,'default'], [12346,'localhost'], [12347,'0.0.0.0']];
const hosts = [{}, {host:'localhost'}, {host:'0.0.0.0'}];
const clientsDone = () => ports.map(([port,whichserver]) => hosts.map(host => new Promise((resolve, reject) => {
let opts = {...host, port:port};
net.createConnection(opts)
.on('error', e => (console.log(opts, 'to:'+whichserver, 'error', e.message), reject(e)))
.on('data', d => console.log(opts, 'to:'+whichserver, 'read', d.toString()))
.on('end', () => resolve());
}))).flat();
Promise.all(serversReady())
.then(servers => Promise.allSettled(clientsDone()).then(() => servers))
.then(servers => servers.forEach(s => s.close()));
More on this after work but figured I'd post this little bit sooner rather than later. Will check 18 too.
I quote answer from @richardlau
Node.js no longer re-sorts results of IP address lookups and returns them as-is (i.e. it no longer ignores how your OS has been configured). You can change the behaviour via the verbatim option to dns.lookup() or set the --dns-result-order command line option to change the default.
You could try:
ping localhost
in you cmd or terminal. If it shows:
Reply from 127.0.0.1
it means your system prefer ipv4 address to resolve localhost. If it shows:
Reply from ::1
it means your system prefer ipv6 address.
You could change system setting to prefer ipv4. Node.js will resolve localhost to 127.0.0.1 as you expect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With