I am running node.js as follows:
> http = require('http')
> http.get('http://myhost.local:8080',
function (res) { console.log("RES" + res) }
).on('error', function (e) { console.log("Error:", e) })
> uri = require('url').parse("http://myhost.local:8080")
{ protocol: 'http:',
slashes: true,
auth: null,
host: 'myhost.local:8080',
port: '8080',
hostname: 'myhost.local',
hash: null,
search: null,
query: null,
pathname: '/',
path: '/',
href: 'http://myhost.local:8080/' }
> http.get(uri,
function (res) { console.log("RES" + res) }
).on('error', function (e) { console.log("Error:", e) })
An error is thrown for both the implicit and explicitly parsed URI and I get the following output for both:
Error: { [Error: connect ECONNREFUSED] code: 'ECONNREFUSED', errno: 'ECONNREFUSED', syscall: 'connect' }
The host myhost.local
is an alias for localhost
in /etc/hosts
, being:
127.0.0.1 localhost myhost.local myhost
255.255.255.255 broadcasthost
::1 localhost myhost.local myhost
fe80::1%lo0 localhost myhost.local myhost
EDIT: I tried virtually every permutation for the hosts file, including the most obvious:
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost myhost.local myhost
fe80::1%lo0 localhost
EDIT I should also mention that I have tried this on more than one Mac now.
Although it seems this is a rather common error, I have seen no useful explanations or workarounds. Here are some notable related facts:
$ wget http://myhost.local:8080
works as expected, so it isn't a firewall problem.$ telnet myhost.local 8080
and then manually GET'ing the url works fine, so it's not a weird HTTP problem.I expect the useful system information would include:
$ node -v
v0.9.11
$ uname -a
Darwin myhost.local 12.2.1 Darwin Kernel Version 12.2.1:
Thu Oct 18 12:13:47 PDT 2012; root:xnu-2050.20.9~1/RELEASE_X86_64 x86_64
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.8.2
BuildVersion: 12C3104
$ sudo netstat -nalt | grep LISTEN | grep 8080
tcp6 0 0 ::1.8080 *.* LISTEN
Does anyone have any idea what is going on here, and what a fix might be?
I'm going to post this here in case somebody else has the problem.
Bert Belder, Node.js mailing list:
On your system "myhost.local" resolves to three different addresses (127.0.0.1, ::1, and fe80::1). Node prefers ipv4 over ipv6 so it'll try to connect to 127.0.0.1. Nothing is listening on 127.0.0.1:8080 so the connect() syscall fails with ECONNREFUSED. Node doesn't retry with any of the other resolved IPs - it just reports the error to you. A simple solution would be to replace 'localhost' by the intended destination ip address, '::1'.
Whether this behavior is right is somewhat open for debate, but this is what causes it.
Bert
This stemmed from an issue with Node (though there are ways to work around it), as per the discussion on nodejs/Google Groups, as @alessioalex alluded in his answer. A useful comment per Bert Belder:
there should be a getaddrinfo wrapper that returns more that just the first result
For example,
> require('dns').lookup('myhost.local', console.log)
{ oncomplete: [Function: onanswer] }
> null '127.0.0.1' 4
This is the first of multiple getaddrinfo
results passed to Node. It seems that nodejs only uses the first item of the getaddrinfo
call. Bert and Ben Noordhuis agreed in the Groups discussion that there should be a way to return more than just the first result with the getaddrinfo
wrapper.
Contrast python, which returns all results from getaddrinfo
:
>>> import socket
>>> socket.getaddrinfo("myhost.local", 8080)
[(30, 2, 17, '', ('::1', 8080, 0, 0)),
(30, 1, 6, '', ('::1', 8080, 0, 0)),
(2, 2, 17, '', ('127.0.0.1', 8080)),
(2, 1, 6, '', ('127.0.0.1', 8080)),
(30, 2, 17, '', ('fe80::1%lo0', 8080, 0, 1)),
(30, 1, 6, '', ('fe80::1%lo0', 8080, 0, 1))]
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