SetTimeout Function: Can't set headers after they are sent to the client

Viewed 299

This code should write a file, run it in a spawn process, and then kill the spawn process if the timeout is reached. However, anytime it triggers the timeout, I get an [ERR_HTTP_HEADERS_SENT] error.

$.get('/path', function(req, res){

// data = contents of a file

fs.writeFile(filename, data,  (err) => {

const child_process = spawn(//run file, {detached: true});

  const wait = setTimeout(function() {
    try {
      process.kill(-runner.pid);
      return res.send("Timeout Message");
    }
  }, 3000);

  runner.on('exit', (code, signal) => {
    clearTimeout(wait);
    fs.unlink(filename, (err) => {
      if (code === 0) return res.send("Good");
      else return res.send('Fail');
    });
  });

  runner.stdout.on('data', function(data) {
    console.log('stdout: ' + data);
  });

  runner.stderr.on('data', function(data) {
    console.log('stderr: ' + data);
  });

  runner.on('error', (err) => {
    clearTimeout(wait);
    fs.unlink(filename, (error) => {
      return res.send("Error");
    });
  });
});

}

Your help is appreciated!

1 Answers

You're ending the request twice. Which is the reason why you get ERR_HTTP_HEADERS_SENT.

The issue with your code is when the timeout is reached, the .on('exit') is being called and you're responding again, clearTimeout at that point doesn't do anything since that timeout already occurred.

The solution is to either add res.headersSent check before each res.send

runner.on('exit', (code, signal) => {
    clearTimeout(wait);
    fs.unlink(filename, (err) => {
      if (code === 0) return !res.headersSent && res.send("Good");
      else return !res.headersSent && res.send('Fail');
    });
  });

Or check whether a timeout has ocurred and respond in exit event instead on timeout callback. Or just kill the process with a specific signal, and test that signal on kill event.

 const wait = setTimeout(function() {
      runner.kill('SIGKILL'); // Use whatever signal you prefer
  }, 3000);

 runner.on('exit', (code, signal) => {
    clearTimeout(wait);
    if(signal === 'SIGKILL')
         return res.send("Timeout Message");
    else if (code === 0) 
         return res.send("Good");
    else return res.send('Fail');
    fs.unlink(filename, (err) => {});
  });
Related