Search code examples
javascriptgoogle-chromeherokuheadless

Heroku - Headless Chrome - Connection Refused


I am currently working with the: Heroku Build Pack for headless chrome. https://github.com/heroku/heroku-buildpack-google-chrome/

I'm encountering this infuriating error where my node script (show below) cannot connect to the chrome instance. I get a pretty definitive error being:

{ Error: connect ECONNREFUSED 127.0.0.1:30555
    at Object.exports._errnoException (util.js:1018:11)
    at exports._exceptionWithHostPort (util.js:1041:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1090:14)
  code: ‘ECONNREFUSED’,
  errno: ‘ECONNREFUSED’,
  syscall: ‘connect’,
  address: ‘127.0.0.1’,
  port: 30555 }

My node super simple script:

CDP((client) => {
    // extract domains
    // const {Network, Page} = client;
    const Network = client.Network
    const Page = client.Page
    // setup handlers
    Network.requestWillBeSent((params) => {
        console.log(params.request.url);
    });
    Page.loadEventFired(() => {
        client.close();
    });
    // enable events then start!
    Promise.all([
        Network.enable(),
        Page.enable()
    ]).then(() => {
        return Page.navigate({url: 'https://www.something.com/'});
    }).catch((err) => {
        console.error(err);
        client.close();
    });
}).on('error', (err) => {
    // cannot connect to the remote endpoint
    console.error(err);
});

Has anyone had any luck getting this type of thing to work?


Solution

  • Alright I figured it out. When deploying to heroku, I was using two different Procs in the Procfile. One for web which was launching the Node script. And another for launching the headless chrome daemon.

    On heroku, those two different procs don't even share the same dyno. Meaning they we're on totally separate "boxes" - at least in theory. This resulted in them having different ports set in the ENVs (not that it even mattered at that point - they might as well be in different continents)

    Solution:

    Have the node script start the actual headless chrome, then ultimately connect to that child process using the CDP interface.

    Also - if you're here and also curious about documentation for the CDP interface for node - it doesnt exist at the moment. Your best option, which is actually pretty good, is: https://chromedevtools.github.io/debugger-protocol-viewer/

    Happy hunting.

    Edit:

    Example of how we handled launching the chrome child process from the application source

    const spawn = require('child_process').spawn
    
    
    spawn('/path/to/chrome/binary',[{`--remote-debugging-port=${process.env.PORT}`]) // Set by heroku
    .on('close', () => console.log('CHROME_PROCESS_CLOSE'))
    .on('error', e => console.log('CHROME_PROCESS_ERROR', e))
    .on('exit', (e, z, a) => console.log('CHROME_PROCESS_EXIT', e, z, a))
    .on('data', () => {})