node.jspostkoa

Why is timeout request ignored in this Nodejs request


Let's assume you have a server in koa and you have a POST route which takes ~ 3 minutes to return a request.

According to the various sources, you set-up your server with the timeout by setting up

let app = new Koa();
let server=app.listen(3000);
server.timeout=5*60*1000; // set to 5 minutes

On the client side, you set-up a timeout as well and you send the post request with the timeout

  const request = await axios.post('https://somewebsite.herokuapp.com/testkeepalive', {}, {timeout:240000})
  console.log('request is ', request)

Why when sending the above, on the server it still returns a timeout error and doesn't execute as intended?

enter image description here

Info about /testkeepalive

  .post('/testkeepalive', async ctx => {
    console.log('request received')
    ctx.request.socket.setTimeout(5 * 60 * 1000)
    ctx.request.socket.setKeepAlive(true)
    await delay(3 * 60 * 1000) // we delay 3 mintues before returning the request
    ctx.body = "nice"
    ctx.status = 202
  })

Client side error: enter image description here

EDIT: Code above is correct. The only issue was that I was using Heroku which times out to 30seconds. Moving the server elsewhere (e.g AWS EC2 + deploy server with https ssl certificate) made it work.

If you still want to use heroku, you can only do it by implementing background jobs. https://devcenter.heroku.com/articles/background-jobs-queueing


Solution

  • Axios is showing you ERR_NETWORK so I think that means that some intermediary (perhaps a proxy in your hosting environment) has closed the socket.

    Below is a test app I wrote that lets me separately set the serverTimeout, the clientTimeout and the serverDelay before sending a response. This allows you to simulate either client or server timeout and see exactly what the axios response is. This is generic Express since I'm not a koa person, but presumably, it is the same http server object in either case.


    With these values configured such that the serverDelay is less than both serverTimeout and clientTimeout:

    const serverTimeout = 10 * 1000;
    const clientTimeout = 8 * 1000;
    const serverDelay = 7 * 1000;
    

    Then, I get the appropriate response from the server (no error).


    With these values configured such that the serverTimeout is shorter than the serverDelay:

    const serverTimeout = 5 * 1000;
    const clientTimeout = 8 * 1000;
    const serverDelay = 7 * 1000;
    

    Then, I get Error socket hang up ECONNRESET.


    With these values configured such that the clientTimeout is shorter than the serverDelay or serverTimeout:

    const serverTimeout = 10 * 1000;
    const clientTimeout = 5 * 1000;
    const serverDelay = 7 * 1000;
    

    Then, I get Error timeout of 5000ms exceeded ECONNABORTED.


    So, it all seems to be working for me with no extra infrastructure in between the client and server. So, that plus the ERR_NETWORK error you see from Axios makes me think that a proxy in your hosting infrastructure is responsible for the error.


    Here's the code I used:

    import express from 'express';
    import axios from 'axios';
    
    const serverTimeout = 10 * 1000;
    const clientTimeout = 8 * 1000;
    const serverDelay = 7 * 1000;
    
    function delay(t) {
        return new Promise(resolve => {
            setTimeout(resolve, t);
        })
    }
    
    const app = express();
    
    app.get("/timeout", async (req, res) => {
        await delay(serverDelay);
        res.send("Hello");
    });
    
    const server = app.listen(80);
    server.timeout = serverTimeout;
    
    
    try {
        let result = await axios.get("http://localhost/timeout", { timeout: clientTimeout });
        console.log("Got Result", result.data);
    } catch (e) {
        console.log("Error", e.message, e.code);
    }
    server.close();
    

    Note also that there is no need to set a timeout separately on each incoming socket. The server timeout itself will handle that for you.