In express app (serving as API server) I would like on router level to know that client that is been currently served was disconnected because of the socket timeout and keep the response for the next time the client sends the request. This specific route sometimes leads to some long operation. What would be the proper way to handle such flow (without switching the client to polling) ?
CodePudding user response:
First off, to keep a response for a specific client, you will need some sort of server-side session object and corresponding client cookie to store the client-specific response in.
Then, you probably need to determine some amount of time for which you want to keep this result (how long until it gets stale?).
Third, Express by default does not timeout client requests. So, unless you set a timeout in Express, if you're getting a timeout, then it's either coming from the client or from your hosting provider. In some ways, it doesn't really matter where it comes from. You can detect that the socket has been closed, but no Express request has been sent. See the code below for how to do that.
const kMaxSaveResponseTime = 10 * 60 * 1000; // 10 minutes
app.get("/someRequest", (req, res) => {
const priorResult = req?.session?.priorResult?.expirationTime;
if (priorResult && priorResult >= Date.now()) {
// if there is a prior result that hasn't yet expired, use it
let data = req.session.priorResult.data;
delete req.session.priorResult;
res.send(data);
return;
}
// otherwise calculate new result and attempt to send it
// simulate some function that takes a long time
// The setTimeout() here is just to simulate taking awhile
setTimeout(() => {
// We have the eventual result here
let data = someDateWeBuilt;
// See if the request socket is still alive
// If not, save the result we just finished calculating
if (res.socket.destroyed) {
// client connection has already been killed
// save the data we have for next connection
req.session.priorResult = {
expirationTime: Date.now() kMaxSaveResponseTime,
data: data
};
} else {
// connection still alive, send the result
res.send(data)
}
}, 5000);
});
This assumes you are using express-session
for server-side session state. It assumes the client has already established a valid session.
The general idea is that you first check to see if there's a non-expired priorResult already in the session. If so, you grab that data, removed it from the session and send it as the response. If not, you do whatever time consuming thing you do to calculate the eventual result and when you finally have that result, you check res.socket.destroyed
to see if the client socket is still alive or not. If it is not, you cache the data in the session. If it is still alive, you send the response.