Why does settimeout break Expressjs - javascript

I have the following code in express.js
// POST api/posts/:id
exports.post = function(req, res){
req.body[0].id = posts.length + 1;
posts.push(req.body[0]);
console.log(posts);
fs.writeFileSync("json/posts.json", JSON.stringify(posts));
setTimeout(function(){
res.set.apply(res, utils.contentType);
res.json(req.body[0]);
}, utils.randomNumberBetween(1000, 3000));
};
When I post a simple json object I receive the following error.
org.apache.http.NoHttpResponseException: localhost:9000 failed to respond
If I remove the settimeout everything works as expected. I have used settimeout in other places like so:
// GET api/posts
exports.get = function(req, res){
setTimeout(function(){
res.set.apply(res, utils.contentType);
res.json(posts);
}, utils.randomNumberBetween(1000, 3000));
};
So I'm not understanding why it would break when posting. Any thoughts??

This is because the application making the request expects a response from express within a certain amount of time, say 5 seconds. If it does not receive anything back it thinks something went wrong. One thing you can do to resolve this is change the amount of time your "sending/requesting" application waits/listens for a response.

Related

node.js express: Is there a way to cancel http response in middleware?

I am writing a timeout middleware with express in node.js.
app.use((req, res, next) => {
res.setTimeout(3000, () => {
console.warn("Timeout - response end with 408")
res.status(408).json({ "error": "timeout 408" });
// !!! error will happen with next function when call like `res.send()`:
// Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
next()
})
If there's an endpoint that takes more than 3000 ms, my middleware will repsond with 408. However, the next function will respond again. I don't want to check if the response has been already sent by res.headersSent api every time.
Is there a better way to handle this - like the title said - to cancel the next response in the middleware?
It's your own code in the response handler that is still running (probably waiting for some asynchronous operation to complete). There is no way to tell the interpreter to stop running that code from outside that code. Javascript does not have that feature unless you put that code in a WorkerThread or a separate process (in which case you could kill that thread/process).
If you're just trying to suppress that warning when the code eventually tries to send its response (after the timeout response has already been sent), you could do something like this:
app.use((req, res, next) => {
res.setTimeout(3000, () => {
console.warn("Timeout - response end with 408")
res.status(408).json({ "error": "timeout 408" });
// to avoid warnings after a timeout sent,
// replace the send functions with no-ops
// for the rest of this particular response object's lifetime
res.json = res.send = res.sendFile = res.jsonP = res.end = res.sendStatus = function() {
return this;
}
});
next();
});

Redirect after already used response Node js

I want to render some page and after certain amount of seconds it needs to be redirected to another page. However, I get an "Can't set headers after they are sent." From what I read, the response should be used only once, though I'm not sure if this is true. I would appreciate any help or solution to this particular problem.
app.get('/', function (req, res) {
var message = req.flash();
res.render('index', { message: req.flash('message'), hassError: message.length > 0} );
});
app.get('/error', function (req, res) {
var timeout = 3000;
setTimeout(function (err) {
console.log('finished');
if (err)
throw err;
console.log('################');
res.redirect('/');
}, timeout);
res.render('partials/404');
});
"finished" gets printed and then it throws error, but not the err specified in callback.
You can't use this approach - rendering means sent to browser with headers, then the HTTP request is closed by browser. You can't send a second response.
You'll have to do the timeout logic in JavaScript on the page, not server:
<script>
var timeout = 3000;
setTimeout(function () {
window.location = "http://www.yoururl.com";
}, timeout);
</script>
You can't send multiple responses to a single request. It's like if someone asks you for a burger, you give them a burger, and they walk away. Some time later you lob another burger at the back of their head. They aren't going to catch it.
In this case, you'll need to handle redirecting on the client-side. If this is just a regular page render, you can use the refresh attribute of a meta tag.
<meta http-equiv="refresh" content="2; url=http://path/to/my/page/" />
If this is an AJAX request, you'll need to have the client perform the redirect after a delay.
The redirect you are trying to send should be done either on the server side right when the user hits the server, or done on the browser after the 404 page has loaded.

AngularJS: Issue with http chain calling and NodeJS: issue with using variable in multiple http call

I have a get call and a post call in my node.js file, both of which use the same variable that I initialized to an empty string outside these calls. In the post call, I set the variable, while in the get call, I return the value of the variable to my clientside angularjs that is requesting the value. In my angularjs file, I make the post call first and then the get call, which means the value should be set and should be available when the get call is issued and returns. Here's what I'm doing:
NodeJS
var myUrl= "";
app.post('/post', function(req, res){
myUrl = res.url;
});
app.get('/get, function(req, res){
res.json({result: myUrl});
});
AngularJS:
var promise = $http.post('/post')
.then(function(response){
return $http.get('/get');
}).then(function(response){
console.log(response.data.result);
});
I've tried AngularJS promise chain calling but it still doesn't work. The problem I'm having is that when I make the get call in the first round of requests, the url variable hasn't been set yet even though the post call has been already issued, so the get call returns an empty string. In the second round of requests, the get call returns the value that has been set from the first post call, and so on and so forth.
Any ideas on why this is happening and suggestions on how to solve this issue so that the get call returns the value that is set in the post call in the same round of requests (the get call is issued when the post call is done)? I'm fairly new to NodeJS so any help is appreciated!
Your angular code is fine, though you have missed a small thing. I modified your code and tested, it works as expected.
Solution:
var myUrl= "";
app.post('/post', function(req, res){
//not res.url
myUrl = req.url;
//Your missed this!
res.status(204).end();
});
app.get('/get, function(req, res){
res.json({result: myUrl});
});
Explanation:
Without res.status(204).end(); or res.send({}) or res.json({}) the /post call just updates the url and then hangs there and does nothing, never returns and will eventually timeout. So when next time you call /get you get the URL.
You must consider the fact that all your route handlers are just middleware and you must generate a response or execute next middleware.
In your case, you wanna handle the request and want to end there, it was required to send some response. so we sent res.status(204).end();, meaning: There is no content to serve
Hope this helps!

Waiting for MongoDB findOne callback to complete before finishing app.get()

I'm relatively new to Javascript and I am having trouble understanding how to use a MongoDB callback with an ExpressJS get. My problem seems to be if it takes too long for the database search, the process falls out of the app.get() and gives the webpage an "Error code: ERR_EMPTY_RESPONSE".
Currently it works with most values, either finding the value or properly returning a 404 - not found, but there are some cases where it hangs for a few seconds before turning the ERR_EMPTY_RESPONSE. In the debugger, it reaches the end of the app.get(), where it returns ERR_EMPTY_RESPONSE, and after that the findOne callback finishes and goes to the 404, but by then it is too late.
I've tried using async and introducing waits with no success, which makes me feel like I am using app.get and findOne incorrectly.
Here is a general version of my code below:
app.get('/test', function (req, res) {
var value = null;
if (req.query.param)
value = req.query.param;
else
value = defaultValue;
var query = {start: {$lte: value}, end: {$gte: value}};
var data = collection.findOne(query, function (err, data) {
if (err){
res.sendStatus(500);
}
else if (data) {
res.end(data);
}
else{
res.sendStatus(404);
}
});
});
What can I do to have the response wait for the database search to complete? Or is there a better way to return a database document from a request? Thanks for the help!
You should measure how long the db query takes.
If it's slow >5sec and you can't speed it up, than it might be a good idea to decouple it from the request by using some kind of job framework.
Return a redirect the url where the job status/result will be available.
I feel silly about this, but I completely ignored the fact that when using http.createServer(), I had a timeout set of 3000 ms. I misunderstood what this timeout was for and this is what was causing my connection to close prematurely. Increasing this number allowed my most stubborn queries to complete.

ExpressJS: How to know when a request has finished?

In an ExpressJS set up on top of NodeJS, I have a bit of code like this:
app.get('/keys/readresults/:keyname', basic_auth, function(req,res){
res.writeHead(200, {'content-type': 'text/json'});
setInterval(
go_to_database_and_get('stuff', function (database_replies) {
res.write(database_replies)
})
,5000);
});
The code is wrote like that for simplicity (if anyone wants the real code I'm happy to post it in some pastebin).
What happens when I curl -u username:password http://localhost:4000/keys/readresults/key is exactly what I wanted to happen: I get a 200 status header, it checks the database for any results I want and writes the response back to me, then waits 5 seconds and searches for new results in the database and writes the response again, and so on every interval of 5 seconds.
The problem is that when I exit curl, the loop keeps on going forever. How do I tell express or nodejs that it's supposed to end that loop (with clearInterval I guess) as soon as the request has ended ?
req.on("close")
So simply
app.get('/keys/readresults/:keyname', basic_auth, function(req,res){
res.writeHead(200, {'content-type': 'text/json'});
var no = setInterval(
go_to_database_and_get('stuff', function (database_replies) {
res.write(database_replies)
});
,5000);
req.on("close", function() {
clearInterval(no);
});
});
req.on('close', ...) no longer works in Express 4. Use the on-finished middleware.
With express 4.X it is req.on("end", () => {}), it is better to add this as a express middleware.
Yeah but on-finished npm package works too.

Categories

Resources