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

[Bug]: browserWSEndpoint with localhost path throws ECONNREFUSED error #8873

Open
timmhayes opened this issue Aug 31, 2022 · 7 comments
Open

Comments

@timmhayes
Copy link

Bug description

Steps to reproduce the problem:

  1. Open Chrome with remote debugging enabled:
    "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
  2. Get webSocketDebuggerUrl by opening the following page using localhost:
    http://localhost:9222/json/version
  3. Connect to existing Chrome instance using webSocketDebuggerUrl value:
    const browser = await puppeteer.connect({browserWSEndpoint: 'WEBSOCKETVALUE'})

Expected behavior:
Puppeteer connects to existing instance of Chrome.

Actual behavior:
Puppeteer throws ECONNREFUSED error (see output below).

Important Details

  • I am connecting to my installed version of Chrome 105.0.5195.54 (Official Build) (64-bit) on Windows, not Chromium.
  • This worked on 16.2.0 and broke with the 17.0.0 update.
  • The workaround is to switch the path to ws://127.0.0.1, which works as expected. But the ws://localhost path has always worked and this change in 17.0.0 broke my existing scripts.

Puppeteer version

17.0.0

Node.js version

18.4.0

npm version

8.12.1

What operating system are you seeing the problem on?

Windows

Relevant log output

ErrorEvent {
  [Symbol(kTarget)]: WebSocket {
    _events: [Object: null prototype] { open: [Function], error: [Function] },
    _eventsCount: 2,
    _maxListeners: undefined,
    _binaryType: 'nodebuffer',
    _closeCode: 1006,
    _closeFrameReceived: false,
    _closeFrameSent: false,
    _closeMessage: <Buffer >,
    _closeTimer: null,
    _extensions: {},
    _paused: false,
    _protocol: '',
    _readyState: 3,
    _receiver: null,
    _sender: null,
    _socket: null,
    _bufferedAmount: 0,
    _isServer: false,
    _redirects: 0,
    _url: 'ws://localhost:9222/devtools/browser/2cdb3a8e-eeb3-421f-92d6-dc4943256db5',
    _originalUnixSocket: false,
    _originalSecure: false,
    _originalHostOrSocketPath: 'localhost:9222',
    _req: null,
    [Symbol(kCapture)]: false
  },
  [Symbol(kType)]: 'error',
  [Symbol(kError)]: Error: connect ECONNREFUSED ::1:9222
      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1237:16) {
    errno: -4078,
    code: 'ECONNREFUSED',
    syscall: 'connect',
    address: '::1',
    port: 9222
  },
  [Symbol(kMessage)]: 'connect ECONNREFUSED ::1:9222'
}
@timmhayes timmhayes added the bug label Aug 31, 2022
@OrKoN
Copy link
Collaborator

OrKoN commented Aug 31, 2022

So it seems in NodeJS localhost resolves to an IPv6 but the browser is actually listening on the IPv4. That only worked for a few releases because we needed a work-around for Firefox #8825 (workaround was not ideal since it required an extra explicit DNS lookup). I think the ws endpoint returned by http://localhost:9222/json/version is not actually correct as the browser is not listening on all IPs. I don't think it also worked in v14.1.2 or earlier and it probably still works in Node 16. I think Node 18 is the version where Node started to prefer IPv6 over IPv4 for DNS resolution.

@OrKoN
Copy link
Collaborator

OrKoN commented Aug 31, 2022

Filed crbug.com/1358656

@whimboo
Copy link
Collaborator

whimboo commented Sep 2, 2022

Interesting case! I've checked with our Firefox implementation and the /json/version end-point works as expected and reports the IP address.

@Kikobeats
Copy link
Contributor

I'm experiencing this issue too. Not sure how to write a proper test for it (it looks kind of random)

@OrKoN
Copy link
Collaborator

OrKoN commented Dec 1, 2022

@Kikobeats you should not have the problem if you only use the IP address 127.0.0.1 to access the endpoint. The Chromium endpoint incorrectly defines that it is listening on localhost whereas it only listens on the IPv4 address that maps to local host. Newer NodeJS versions/system configuration might route localhost to the IPv6 address where the browser actually does not have a server listening.

@Kikobeats
Copy link
Contributor

@OrKoN In my case it was a bug on user-code: I have some logic to automatically reconnect the browser under disconnection and that was keeping the web socket connection forever.

Happy to say it's fixed for me
microlinkhq/browserless#429

🙂

okkays added a commit to usdoj-crt/crt-portal that referenced this issue Jan 13, 2023
- 🌎 We currently connect to local for tests via 127.0.0.1
- ⛔ This only works for ipv4. Some versions of node, and thus pupeteer,
  only support ipv6 (see:
  puppeteer/puppeteer#8873)
- ✅ This commit uses localhost instead, which can resolve to either
  ipv4 or ipv6 as needed.
@bharatbots
Copy link

@Kikobeats you should not have the problem if you only use the IP address 127.0.0.1 to access the endpoint. The Chromium endpoint incorrectly defines that it is listening on localhost whereas it only listens on the IPv4 address that maps to local host. Newer NodeJS versions/system configuration might route localhost to the IPv6 address where the browser actually does not have a server listening.

This is the correct reason if it happens on the newer versions of node.js. I fixed it by replacing localhost with 127.0.0.1

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

No branches or pull requests

5 participants