Home > Back-end >  Abandoned http requests after server.close()?
Abandoned http requests after server.close()?

Time:12-08

I have a vanilla nodejs server like this:

let someVar // to be set to a Promise

const getData = url => {
  return new Promise((resolve, reject) => {
    https.get(
      url,
      { headers: { ...COMMON_REQUEST_HEADERS, 'X-Request-Time': ''   Date.now() } },
      res => {
        if (res.statusCode === 401) return reject(new RequestError(INVALID_KEY, res))
        if (res.statusCode !== 200) return reject(new RequestError(BAD_REQUEST, res))

        let json = ''
        res.on('data', chunk => json  = chunk)
        res.on('end', () => {
          try {
            resolve(JSON.parse(json).data)
          } catch (error) {
            return reject(new RequestError(INVALID_RESPONSE, res, json))
          }
        })
      }
    ).on('error', error => reject(new RequestError(FAILED, error)))
  })
}
const aCallback = () =>
  console.log('making api call')
  someVar = getData('someApiEndpoint')
    .then(data => { ... })
}
const main = () => {
  const server = http.createServer(handleRequest)
  anInterval = setInterval(aCallback, SOME_LENGTH_OF_TIME)

  const exit = () => {
    server.close(() => process.exit())
    log('Server is closed')
  }

  process.on('SIGINT', exit)
  process.on('SIGTERM', exit)

  process.on('uncaughtException', (err, origin) => {
    log(`Process caught unhandled exception ${err} ${origin}`, 'ERROR')
  })
}
main()

I was running into a situation where I would ctrl-c and would see the Server is closed log, followed by my command prompt, but then I would see more logs printed indicting that more api calls are being made.

Calling clearInterval(anInterval) inside exit() (before server.close()) seems to have solved the issue of the interval continuing even when the server is closed, so that's good. BUT:

From these node docs:

Closes all connections connected to this server which are not sending a request or waiting for a response.

I.e., I assume server.close() will not automatically kill the http request.

What happens to the http response information when my computer / node are no longer keeping track of the variable someVar?

What are the consequences of not specifically killing the thread that made the http request (and is waiting for the response)?

Is there a best practice for cancelling the request?

What does that consist of (i.e. would I ultimately tell the API's servers 'never mind please don't send anything', or would I just instruct node to not receive any new information)?

CodePudding user response:

There are a couple things you should be aware of. First off, handling SIGINT is a complicated thing in software. Next, you should never need to call process.exit() as node will always exit when it's ready. If your process doesn't exit correctly, that means there is "work being done" that you need to stop. As soon as there is no more work to be done, node will safely exit on its own. This is best explained by example. Let's start with this simple program:

const interval = setInterval(() => console.log('Hello'), 5000);

If you run this program and then press Ctrl C (which sends the SIGINT signal), node will automatically clear the interval for you and exit. This auto-exit behavior changes as soon as you listen for the SIGINT event:

const interval = setInterval(() => console.log('Hello'), 5000);

process.on('SIGINT', () => {
  console.log('SIGINT received');
});

Now if you run this program and press Ctrl C, you will see the "SIGINT received" message, but the process will never exit. When you listen for SIGINT, you are telling node "hey, I have some things I need to cleanup before you exit". Node will then wait for any "ongoing work" to finish before it exits. If node doesn't eventually exit on it's own, it's telling you "hey, I can see that there are some things still running - you need to stop them before I'll exit".

Let's see what happens if we clear the interval:

const interval = setInterval(() => console.log('Hello'), 5000);

process.on('SIGINT', () => {
  console.log('SIGINT received');
  clearInterval(interval);
});

Now if you run this program and press Ctrl C, you will see the "SIGINT received" message and the process will exit nicely. As soon as we clear the interval, node is smart enough to see that nothing is happening, and it exits. The important lesson here is that if you listen for SIGINT, it's on you to wait for any tasks to finish, and you should never need to call process.exit().

As far as how this relates to your code, you have 3 things going on:

  • http server listening for requests
  • an interval
  • outgoing https.get request

When your program exits, it's on you to clean up the above items. In the most simple of circumstances, you should do the following:

  • close the server: server.close();
  • clear the interval: clearInterval(anInterval);
  • destroy any outgoing request: request.destroy()

You may decide to wait for any incoming requests to finish before closing your server, or you may want to listen for the 'close' event on your outgoing request in order to detect any lost connection. That's on you. You should read about the methods and events which are available in the node http docs. Hopefully by now you are starting to see how SIGINT is a complicated matter in software. Good luck.

  • Related