How to set cache headers only if there isn't an error in sending the response in Node.js - node.js

I am doing this essentially:
app.get('/:id.:ext', (req, res) => {
const remote = bucket.file(`images/${req.params.id}.${req.params.ext}`)
// if (isProd)
// res.set('Cache-Control', 'public, max-age=604800')
remote.createReadStream({ validation: false })
.on('error', error => {
console.log(error)
res.send(`data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7`)
})
.pipe(res)
.on('end', () => {
res.set('Cache-Control', 'public, max-age=86400')
})
})
When there is an error, I just return an empty image. But in this case, I don't want to set the cache headers because I don't want the blank image to be cached.
So I try setting the cache headers after the response is sent, but they don't go through.
If I set the cache headers before piping the response, I cache the error in case of error.
Wondering how to get around this.

You can't do what you're trying to do the way you're trying to do it. The sequence of an http response is to send the http headers, then start sending the respond body. So if you're piping a response body (like you are), you HAVE to send the headers first. You can't start sending the response body, then changing your mind about the headers. They've already been sent.
And, you can't send headers after the http library has already starting sending the respond body. Starting to send the response body writes out the currently stored state of the http headers that go with this response and then starts writing the response.
As best I know, the only way to deal with an error that occurs in the middle of sending the http response body is to close the http connection prematurely. The client will see the socket close without seeing the end of the http response and will understand that it got a terminated, unfinished response. You don't get a chance to send another response at that point. Error handling for that case will need to be client-side in order to decide what to do.
Another option is to prefetch all the data you want to send to the client BEFORE you send anything. This allows you the most chances to determine if anything is going to cause an error before you've started sending the http response and then you can craft the entire response to match your error condition. You obviously can't use something like .pipe(res) if you're going to do that. Instead, you've have to load the entire response into memory (or at least a chunk of the response if you're going to send it in chunks) and only when that has been successfully pre-flighted and loaded and ready to go do you start sending the response.
Also, another way to avoid getting an error image cached is to do a 301 (temporary redirect) to the error image URL rather than return it as the response to the original request. Then, when the browser loads that redirected URL and gets the image, it will not cache it as the original URL.

Related

Sending server response when an events occurs but this cause a (cannot modify headers, headers have already been sent) error

In my project the user makes a request to this route some complex logic happens and the app sends a response to the user when a move-made event occurs.
the code works fine the response is sent to the user but the app refuses to exit the route handler, this makes the server to crash with an error message of (cannot modify headers, headers have already been sent) when I tried to visit the route again
this is my code
app.post('/api/play', (req, res) =>{
gameStart.humanPlay(req.body)
gameStart.on("move-made", (moves) => {
res.send(moves)
})
})
Putting the event listener inside the play route creates multiple event listeners each time the client send a post request to the play route, which sends multiple response to the client this is what caused the error message (cannot modify headers headers have already been sent).
To fix this error pass, res as a argument of humanPlay and delete the event listener from your play route
app.post('/api/play', (req, res) =>{
gameStart.humanPlay(req.body, res)
})
and send the respond from within the class
res.send(this.moves)

Access header values calculated by node http server

Нello, please take a look at this simple file server.js:
require('http').createServer((req, res) => {
res.end('hiii');
console.log('Response headers:', res.getHeaders());
}).listen(80);
Navigating to localhost:80 in my browser hits this endpoint. This causes the response of hiii to appear in the browser, and also the headers to be logged to stdout.
The strange thing is, the headers logged to stdout disagree with the headers the browser received.
Stdout shows me an object representing 0 headers:
Response headers: [Object: null prototype] {}
Developer tools show me that in fact, 3 response headers were received:
What accounts for this difference? I understand that the 3 headers shown in chrome are very fundamental to http. Is chrome receiving 0 headers, but filling them in by default? Is node's http library filling these headers in by default? If that's the case, why aren't they exposed via res.getHeaders()? Are these headers being calculated at some lower level, as in C libraries? If so is there any means of exposing these values?
I tried the following in case there is some kind of async delay where the headers are calculated:
require('http').createServer((req, res) => {
res.end('hiii');
setTimeout(() => console.log('Response headers:', res.getHeaders()), 3000);
}).listen(80);
But nonetheless, 0 headers are sent to stdout.
Somewhere, these 3 headers are being calculated! How can I access these calculated header values??
I found them under res.socket._httpMessage._header
require('http').createServer((req, res) => {
res.setHeader('HELLO', 'WORLD')
res.end('hiii');
console.log(res.getHeaders())
console.log(res.socket._httpMessage._header)
}).listen(8000);
You can't get them using getHeaders because they are not set using the regular API but they are sent by nodejs internaly.
If you define a header yourself using setHeader then you'll be able to retrieve it using getHeaders
Response headers are not generated by the client since it needs them to be able to process the response. How can the client read the request if it doesn't know the size of the body (Content-Length).
These communication rules are defined in the HTTP RFC and the server must implement them so the client can understand the message it receives.
NodeJS calculates these information internaly (source code exampe here) and probably sends them somehow in the socket without storing them in the high level API that you are using. (another source code example)

Can't create a listener to the database while also being able to update it. (using References.on and References.put)

I'm trying to display a list of comments on my react page.
For this I have setup a NodeJS server which loads the data from Firebase and passes it on to React. I am able to get it to load the comments list and display them, but when I try to add a comment, the server crashes with the following error:
#firebase/database: FIREBASE WARNING: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
This is because I am using:
firebase.database().ref('my-path').on("value", ...)
However, if I use firebase.database().ref('my-path').once("value", ...) then I lose the ability to update the comments as soon as a new comment is posted. Is there a way to be able to have a listener attached to the database and still be able to update the contents of that database?
Here is my NodeJS code:
app.get("/comments/:id", (req, res) => {
const itemsRef = firebase.database().ref(`comments/${req.params.id}`);
itemsRef.on('value', (snapshot) => {
let comments = snapshot.val();
return res.status(200).json(comments);
})
})
app.post("/comments/:id", (req, res) => {
const itemsRef = firebase.database().ref(`comments/${req.params.id}`);
itemsRef.push(req.body);
})
The error occurs after the post request is called.
You're sending a response back to the client with:
res.status(200).json(comments)
This sets at least two headers (the status, and the response type) and then sends the response. Next time you get an update from the database, this code runs again, and again tries to send the two headers. But in HTTP all headers must be before the main body of the response. So the second time this code runs, it throws an error.
If you want to keep sending more data to the client, you'll need to use more primitive methods of the response object to prevent sending headers, or other illegal data. While possible, it's more complex than you may think, as the client needs to handle this response stream, which most clients won't.
I'd highly recommend looking at Doug's alternative, which is to just use the Firebase Realtime Database from the client directly. That way you can use the client SDK that it has, which handles this (and many more complexities) behind the scenes.

Difference between response.status() vs. response.sendStatus() in express

What is the difference between response.status() and response.sendStatus() in Express.
I notice that one is used generally for post, get, and other middleware, while the later is used in delete requests. Why is this?
status() sets a HTTP status on the response (as a Javascript object on the server side).
sendStatus() sets the status and sends it to the client.
The usage doesn't depend on the HTTP method of the request. In most cases you would use sendStatus anyway since it's unlikely that the status changes once the request is processed (especially since status code is the first line in a raw HTTP response).
Read more in the docs:
https://expressjs.com/en/4x/api.html#res.sendStatus
res.sendStatus is shorthand for implementing res.send and res.status
the link shared by #freakish explains it all.
res.sendStatus(200); // equivalent to res.status(200).send('OK')
res.sendStatus(403); // equivalent to res.status(403).send('Forbidden')
res.sendStatus(404); // equivalent to res.status(404).send('Not Found')
res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')
As freakish already pointed out, sendStatus sets and sends the status.
This means that if you want to both set a status and send a body you have to use status. For example, you want to set an error status, and send a body with a JSON that explains why the error occured, you first have to set the status (using status), and then send the JSON (using send). If you had already set the status with sendStatus, it is no longer possible to send the JSON, because you already used a form of sent.
sendStatus(code) is a function that duplicates the standard status message corresponding to the provided status code into the body:
app.get('/a', (req, res) => {
res.sendStatus(500)
})
HTTP/1.1 500 Internal Server Error <--- this status message (aka "reason phrase")
<response headers>
Internal Server Error <--- gets duplicated here in body
See here for its implementation. There is no obvious need to do that, because the status message (the text after "500 ") will be there regardless. The above is NOT a normal HTTP idiom. Who knows why it was added into the express response. Moreover, when you send a non-empty body along with a status to a browser, it will render that body rather than a standard stylish page:
res.status(500).send() is sufficient to respond in a standard way:
app.get('/b', (req, res) => {
res.status(500).send()
})
HTTP/1.1 500 Internal Server Error <--- status message
<response headers>
<--- empty body
This is what everyone expects if there is nothing to say beyond status code & message.
no it's not for special routers like delete
The diffrence is res.status() only sets header and when you send response (res.send()) it will send to user
but res.sendStatus() sends the response at the moment without response body

Is There a Way to Check Sent Headers with Node/ Express 2.x?

Is there a way of checking what specific headers have been sent using node/ express 2.x?
I have a file download that works perfectly most of the time, but in a few specific instances I get an error in Chrome (no errors in node):
Duplicate headers received from server
The response from the server contained duplicate headers. This problem is generally the result of a misconfigured website or proxy. Only the website or proxy administrator can fix this issue.
Error 349 (net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION): Multiple distinct Content-Disposition headers received. This is disallowed to protect against HTTP response splitting attacks.
For testing purposes, I'd like to see if a specific header has been sent or not, is there a way of doing this with node.js?
...And because someone's going to ask me about the code I use to set headers, I'm piping a stream as the download and are only setting headers in one spot.
res.setHeader('Content-disposition', 'attachment; filename=' + filename)
res.setHeader('Content-Length', stats.size)
res.setHeader('Content-type', 'application/pdf')
stream.pipe(res)
The HTTP response is a WritableStream. When the stream closes, a finish event is emitted. Thus listening to it does the trick:
res.on('finish', function() {
console.log(res._headers);
});
Much more flexible. Can be put in a middleware or a resource handler.
As #generalhenry stated on my question comments:
stream.pipe(res).on('end', function () {
console.log(res._headers);
});
The above line worked for me.
res.set("Content-disposition", "attachment; filename=\""+file.name+"\"")
This worked for me.

Resources