I wish to put a timeout on my responses. I tried the following:
var timeout = express.timeout // express v3 and below
app.use(timeout(120000));
app.use(haltOnTimedout);
function haltOnTimedout(req, res, next){
if (!req.timedout) next();
}
The problem is that there is some requests which are takes more then 120000 ms (download of big files), and it fine. I want to timeout only requests that no information was transferred on the stream between my application (the server) and the client.
My code above closes all the connection which takes more then 120000 ms.
You can set timeout on the message itself (it will be called if no data is transfered)
req.connection.setTimeout(120000, function () { res.status(500).end(); });
https://nodejs.org/api/http.html#http_message_settimeout_msecs_callback
EDIT:
I've added the 500 status code
just updated your original example for what works for me.
app.use( timeout( 120000 ) );
app.use( haltOnTimedout );
let haltOnTimedout = ( err, req, res, next ) => {
if ( !req.timedout ) next();
// handle timeout / kill process
};
Basically we tapping into the express error handling :
https://expressjs.com/en/guide/error-handling.html
Related
I am using express and Connect Timeout Middleware to handle the timeouts.
It works great, but I the default node http server's timeout is set to two minutes.
Therefore if I want to set my timeout middleware to a value greater than two minutes, I also have to increase the http server timeout to be slightly bigger (otherwise my connect timeout handler is not called)
const app = express();
const http = new Http.Server(app);
http.setTimeout((4 * 60 * 1000) + 1); <-- Must set this
app.use(timeout('4m'));
How can I avoid this ? Am I missing something ?
If you want to use the connect-timeout middleware, you can't avoid it, since the middleware does not change the socket timeout, which defaults to 2 minutes.
There are two possible ways to avoid it, either using server.setTimeout() or request.setTimeout.
In case you only want to change the timeout to a few routes, and leave the default timeout to the rest, the recommended approach is to use: request.setTimeout
app.use('/some-routes', (req, res, next) => {
req.setTimeout((4 * 60 * 1000) + 1);
next();
}, timeout('4m'));
An alternative to setting the req.setTimeout to a value greater than the connect-timeout value, is dropping the connect-timeout middleware and using another work around, which is also not ideal.
You can check this old Node.js issue https://github.com/nodejs/node-v0.x-archive/issues/3460
function haltOnTimedout (req, res, next) {
if (!req.timedout) next()
}
app.use('/some-routes', (req, res, next) => {
req.setTimeout(4 * 60 * 1000); // No need to offset
req.socket.removeAllListeners('timeout'); // This is the work around
req.socket.once('timeout', () => {
req.timedout = true;
res.status(504).send('Timeout');
});
next();
});
app.use(haltOnTimedout);
// But if the timeout occurs in the middle of a route
// You will need to check if the headers were sent or if the request timedout
app.get('/some-routes', async(req, res, next) => {
// some async processing...
await asyncOperation();
if (!res.headersSent) // or !req.timedout
res.send('done');
});
I'm using node with express and request modules. I could call res.json before I started using request to fetch info from another server. However, as soon as I'm trying to use res.json in the callback function in request, I would get the error message that the header has already been sent.
One solution would be to set the format header explicitly as 'application/json', however I don't want to unwrap res.json. Are there any other solutions to this? Has this been caused by the fact that express believes no header has been set and so presumptuously sends one on its own?
Code sample:
`
router.get('/app/:action', function (req, res) {
switch(req.params.action) {
case "search_gifs":
//res.json(["no problem"]);
request(
{/*some params*/},
function (error, response, body) {
res.json(["error"]);return;
}
);
break;//I didn't add break but nothing is happening in default, but I'll try again with break
}
`
As you said, you are getting error like this header has already been sent.
Let me explain you in simple way,
You must be written res.json from multiple places as per your condition.
You are getting this error because res.json is executing multiple times.
When it execute first time it will not give you error but second time it will give you error because response is already sent.
Your code has some loop hole. Debug it.
Let me try to explain you in details with example here.
const express = require('express');
const app = express();
//
// To generate error
//
app.get('/generate_error', function(req, res) {
//
// When check_error query param is true, It will throw you error else
// all good
//
if (req.query.check_error) {
//
// check_error is true so let's send response first here.
//
res.send('Hello World!');
}
//
// If check_error is true then it will try to send response again for
// same request.
// If check_error is false then it will by send response first time
// here so no error
//
res.send('Hello World!');
});
//
// Solution 1 for above case
//
app.get('/ignore_error_1', function(req, res) {
if (req.query.check_error) {
res.send('Hello World!');
} else {
res.send('Hello World!');
}
});
//
// Solution 2 but different with solution 1
//
app.get('/ignore_error_2', function(req, res) {
if (req.query.check_error) {
//
// When you don't have anything to execute after sending response
// then just add return here.
// In other case you have to manage your code as per your
// requirement if you have anything needs to be executed after
// sending response from multiple places.
// NOTE : Make sure you will not execute res.json multiple times
//
return res.send('Hello World!');
}
return res.send('Hello World!');
});
app.listen(3000, function() {
console.log('Example app listening on port 3000!')
});
Just execute this three get urls
/generate_error?check_error=true
/ignore_error_1?check_error=true
/ignore_error_2?check_error=true
I want to have a middleware like Express log request summary end of each route
=>
[POST] /books/commentpart 200 1435.472 ms - 35
I want to log more data in end of each request. But I do not know how to write middleware like this. I have tried a middleware function after all route but it not worked. In each router, I also pass next() call.
app.use(responseTime());
//router
app
.use(users(app, db))
.use(dataLogs(app, db))
.use(category(app, db));
//log middleware
app.use(function(req, res, next) {
var data = {};
var method = req.method;
if(method == 'GET'){
data = req.query;
} else {
data = req.body;
}
var resTime = res.getHeader('X-Response-Time');
log.debug(' [ ' + iduser + ' ] - ' + req.route.path + ' - ' + resTime + ' : ' + JSON.stringify(data));
});
module.exports = app;
So, middlewares, when registered (using app.use, app.all, or etc.) are a queue of rules that apply to incoming requests. Note that this registration happens at the server-start time and is a part of the server itself, not requests.
Once it's all set up and the server is running, any hop (step in the queue, a.k.a middleware) after the first one is called only if the previous hop has called next() explicitly. In other words, if you put a app.use((req, res, next) => {}); at the beginning of all the middlewares, the server is gonna do absolutely nothing! -- this middleware is just swallowing any incoming request and not calling next().
Problem
Now the problem is, how can you register a middleware that:
Applies to all rules, AND
Runs after all the other middlewares
Satisfying the second requirement is not easy. Because as we mentioned above the last hop in this rules queue runs only when all the previous ones have gracefully called next() one after another. And sometimes it doesn't happen for various reasons, including simply forgetting to call next()! And it's hard to enforce people to do that.
Solution
I need to mention what #robertklep said in a comment above bolder. It's using on-finished on res! https://github.com/jshttp/on-finished
Example:
// have this before all the other middlewares
app.use((req, res) => {
console.log("I'll be the first line executed, almost ever!");
onFinished(res, (err) => {
console.log("and I'll be the last one, knowing response"
+ " code which is already sent is: " + res.statusCode);
});
};
What this does is, actually listening on when the response is finished (you can alternatively use on-headers). So it's a whole new dimension of doing a job on express, prependicular to the existing middlewares queue mechanism. Just be careful and enjoy! ;)
middleware is just a function that do something and then pass the request to the next function using "next()". So if you really want to do that you need to catch all of your routes in the same route something like this
app.all('*', (req, res) => {
// do something before
// your async code here
// do something after
});
Finally it worked. Actually, in some routes, i forgot pass next() in the end of each router.
Now i do another method, utilizing responseTime function and it works perfectly without add next() to all every routes. Thanks all!
app.use(responseTime(function (req, res, time) {
var data = {};
var method = req.method;
if(method == 'GET'){
data = req.query;
} else {
data = req.body;
}
log.info(method, iduser, req.path, ' - ', Math.round(time), 'ms', ' : ', JSON.stringify(data));
}))
Recently I started putting response time to my API's.I tried putting to my routes but I am not sure how to use it.My aim is to get the response time in the console for each request.
var responseTime = require('response-time')
app.use(responseTime());
app.route('/getAllUsers').get(users.getUsers);
Can anyone please suggest help.Thanks.
For getting the response time in your routes -
var start = new Date();
router.get('/dummy', function(req, res, next){
console.log('Request took:', new Date() - start, 'ms');
});
output ex - Request took: 1596 ms
You can pass a function that will get called after a request:
app.use(responseTime((req, res, time) => {
console.log(req.method, req.url, time + 'ms');
}));
Or use a more elaborate logger middleware like morgan that also provides the option to log response times.
PROBLEM
I've been looking for request/response timeouts for Express.js but everything seems to be related to the connection rather than the request/response itself.
If a request is taking a long time, it should be timed out. Obviously this shouldn't happen but even a simple mistake as having a route handler without a call to the callback or without res.send(), the browser will keep waiting for a reply forever.
An empty route handler is a perfect example of this.
app.get('/sessions/', function(req, res, callback){});
FIX
I added the following before app.use(app,router); and it seemed to add the timeout functionality. Does anyone have any experience/opinion on this?
app.use(function(req, res, next){
res.setTimeout(120000, function(){
console.log('Request has timed out.');
res.send(408);
});
next();
});
Note that I've set the timeout to 2 minutes.
There is already a Connect Middleware for Timeout support:
var timeout = express.timeout // express v3 and below
var timeout = require('connect-timeout'); //express v4
app.use(timeout(120000));
app.use(haltOnTimedout);
function haltOnTimedout(req, res, next){
if (!req.timedout) next();
}
If you plan on using the Timeout middleware as a top-level middleware like above, the haltOnTimedOut middleware needs to be the last middleware defined in the stack and is used for catching the timeout event. Thanks #Aichholzer for the update.
Side Note:
Keep in mind that if you roll your own timeout middleware, 4xx status codes are for client errors and 5xx are for server errors. 408s are reserved for when:
The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time.
You don't need other npm modules to do this
var server = app.listen();
server.setTimeout(500000);
inspired by https://github.com/expressjs/express/issues/3330
or
app.use(function(req, res, next){
req.setTimeout(500000, function(){
// call back function is called when request timed out.
});
next();
});
An update if one is using Express 4.2 then the timeout middleware has been removed so need to manually add it with
npm install connect-timeout
and in the code it has to be (Edited as per comment, how to include it in the code)
var timeout = require('connect-timeout');
app.use(timeout('100s'));
In case you would like to use timeout middleware and exclude a specific route:
var timeout = require('connect-timeout');
app.use(timeout('5s')); //set 5s timeout for all requests
app.use('/my_route', function(req, res, next) {
req.clearTimeout(); // clear request timeout
req.setTimeout(20000); //set a 20s timeout for this request
next();
}).get('/my_route', function(req, res) {
//do something that takes a long time
});
If you need to test your api, this solotion can you help.
I used this in middleware to test my frontend.
For exmaple: if you need to test loader in frontend.
const router = require('express').Router();
const { data } = require('./data');
router.get('/api/data', (req, res, next) => {
setTimeout(() => {
res.set('Content-Type', 'application/json')
res.status(200).send(data)
next()
}, 2000)
})
module.exports = router;
request.setTimeout(< time in milliseconds >) does the job
https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback
You can try:
return await new Promise((resolve) =>
setTimeout(() => {
resolve(resp);
}, 3000),
);
In above code, 3000 = 3 sec.
Change it according to your requirement.
I have not tried for very long scenarios though. Let me know the results in comments.
Before you set your routes, add the code:
app.all('*', function(req, res, next) {
setTimeout(function() {
next();
}, 120000); // 120 seconds
});