Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPv6 getaddrinfo ENOTFOUND Error #18

Open
dtillner opened this issue Jan 17, 2025 · 4 comments
Open

IPv6 getaddrinfo ENOTFOUND Error #18

dtillner opened this issue Jan 17, 2025 · 4 comments

Comments

@dtillner
Copy link

I'm using supertest to test HTTP requests and would like to use superwstest for WebSocket testing. My HTTP request tests with supertest work as expected. Here's a simplified version of my code:

import request from "supertest";

let server = null;

  before(async () => {
      server = await app.listen(0);
  });

  after(async () => {
      await server.close();
      await server.closeAllConnections();
  });


  test('testquery', async (t) => {
      await request(server)
      .post('/')
     // ...
 });

According to the superwstest documentation, I should be able to replace:

import request from "supertest";

with:

import request from "superwstest";

However, when I do this, I encounter the following error:

Error: getaddrinfo ENOTFOUND [::]
      at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
    errno: -3008,
    code: 'ENOTFOUND',
    syscall: 'getaddrinfo',
    hostname: '[::]',
    response: undefined
  }

Interestingly, if I explicitly request the server with:

await request('http://127.0.0.1:<port>/')

everything works as expected. But a request to:

http://:::<port>/

does not work, suggesting an issue with how IPv6 addresses are being handled.

I've noticed that superwstest includes checks for different Node.js versions. Could there have been a recent change in how Node.js handles IPv6 addresses, which might be causing this issue?

Any guidance or insights on resolving this would be appreciated!

Environment:

Node.js: 23.4.0
Runtime: Docker container

@davidje13
Copy link
Owner

davidje13 commented Jan 18, 2025

I've noticed that superwstest includes checks for different Node.js versions. Could there have been a recent change in how Node.js handles IPv6 addresses, which might be causing this issue?

There was a temporary change in NodeJS where it returned family: 6 instead of family: 'IPv6' in version 18, but that turned out to be a mistake and I believe it has gone back to being IPv6 since then. The fact that it's trying to access the hostname [::] suggests the IPv6 detection is working correctly here.

The error you're seeing is an interesting one, and seems likely to be related to your Docker network setup. I suspect that although NodeJS believes the network is IPv6-capable, in practice it isn't.

A simple workaround would be to listen on an IPv4 address explicitly:

before(async () => {
  server = await app.listen(0, '127.0.0.1');
});

As an aside, the URL format for connecting to an IPv6 address is http://[::]:<port>/ not http://:::<port>/

@dtillner
Copy link
Author

dtillner commented Feb 6, 2025

I investigated this further and found that:

  • The issue is not specific to the Node.js version.
  • This is not just a Docker-related problem—it also occurs on Ubuntu 24.10.

Below are examples of request calls that succeed or fail:

For post requests either with supertest or superwstest

import request from "supertest";
import wsrequest from "superwstest";

...
// ✅ await request('127.0.0.1:' + server.address().port)
// ✅ await request('http://127.0.0.1:' + server.address().port)
// ❌ await request('[::1]:' + server.address().port)await request('[::1]:' + server.address().port)
// ❌ await request('http://[::1]:' + server.address().port)
// ❌ await request(`[${server.address().address}]:${server.address().port}`)
// ❌ await request(`http://[${server.address().address}]:${server.address().port}`)
// ✅ await request(server)

// ✅ await wsrequest('127.0.0.1:' + server.address().port)
// ✅ await wsrequest('http://127.0.0.1:' + server.address().port)
// ❌ await wsrequest('[::1]:' + server.address().port)
// ❌ await wsrequest('http://[::1]:' + server.address().port)
// ❌ await wsrequest(`[${server.address().address}]:${server.address().port}`)
// ❌ await wsrequest(`http://[${server.address().address}]:${server.address().port}`)
// ❌ await wsrequest(server)

console.log(server.address());
// { address: '::', family: 'IPv6', port: 46323 }
...
    

For WebSocket requests with superwstest

import wsrequest from "superwstest";

...
    
// ❌ await wsrequest('127.0.0.1:' + server.address().port) 
// SyntaxError [Error]: Invalid URL: 127.0.0.1:46323/subscriptions

// ✅ await wsrequest('http://127.0.0.1:' + server.address().port)
// ✅ await wsrequest('ws://127.0.0.1:' + server.address().port)

// ❌ await wsrequest('[::1]:' + server.address().port)
// SyntaxError [Error]: Invalid URL: [::1]:46323/subscriptions

// ✅ await wsrequest('http://[::1]:' + server.address().port)
// ✅ await wsrequest('ws://[::1]:' + server.address().port)

// ❌ await wsrequest(`[${server.address().address}]:${server.address().port}`)
// ✅ await wsrequest(`http://[${server.address().address}]:${server.address().port}`)
// ✅ await wsrequest(`ws://[${server.address().address}]:${server.address().port}`)

// ✅ await wsrequest(server)

...

Using request(server) does not work for POST requests with superwstest, while WebSocket requests function correctly.

Interestingly, superwstest supports IPv6 (whereas supertest does not), but it requires a protocol in the URL, while supertest does not require one for IPv4.

The documentation states:

The server URL given should be http(s) rather than ws(s); this will provide compatibility with native supertest requests such as post, get, etc. and will be converted automatically as needed.

Is there logic that dynamically changes the protocol based on the request type? Could this behavior interfere with request(server) when making a POST request over IPv6?

I'm eager to help resolve this issue and am willing to run additional tests or make code changes with some guidance.

@davidje13
Copy link
Owner

After a bit of digging, I found the root issue in superagent (which is used by supertest)

Specifically, this line tries to set the host option in a call to http.request to the hostname from the requested URL, but URL.hostname includes [] wrapping, and http.request's host fails when this is provided:

new URL('http://[::1]:8080').hostname // [::1]

http.request({ port: 8080, host: '[::1]' }) // fails

http.request({ port: 8080, host: '::1' }) // fine

I'd recommend reporting this to superagent, since it does try to be IPv6 compatible (and has plenty of IPv6 handling code elsewhere).


On the differences you noted: supertest will always assume servers are available at http(s)://127.0.0.1:port (even if the server is actually only listening on IPv6 and isn't available at all on IPv4!), whereas superwstest will get the address from the server itself.

When supertest's methods are proxied via superwstest, the server address code from superwstest is still used (hence it attempting to connect via IPv6, triggering the bug in superagent)

@davidje13
Copy link
Owner

I have reported this in the relevant library here: ladjs/superagent#1828

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants