Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inexplicable node.js http throwing connect ECONNREFUSED (IPv6?)

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:

  1. Running $ wget http://myhost.local:8080 works as expected, so it isn't a firewall problem.
  2. Running $ telnet myhost.local 8080 and then manually GET'ing the url works fine, so it's not a weird HTTP problem.
  3. I have no trouble using node.js to connect to other hosts e.g. http://www.google.com

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?

like image 515
Brian M. Hunt Avatar asked Mar 05 '13 15:03

Brian M. Hunt


2 Answers

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

like image 105
alessioalex Avatar answered Nov 03 '22 01:11

alessioalex


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))]
like image 39
Brian M. Hunt Avatar answered Nov 03 '22 01:11

Brian M. Hunt