Н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)
Related
I want to see in Node Exprees enviroment all the headers that are sent to the client
But when i see do this:
app.get("/", (req, res) => {
console.log(res.getHeaders());
});
i only see this :
At the time you're looking at the outgoing headers, those are the only ones that have been added so far. The rest will be added by the code that sends the actual response or by other middleware.
If you want to see all the headers that were eventually added to the response before it was sent, you can monitor the finish event and THEN look at the headers:
app.use(function(req, res, next) {
res.on('finish', () => {
console.log(`request url = ${req.originalUrl}`);
console.log(res.getHeaders());
});
next();
});
This will sometimes not include the date, content-type or content-length headers unless they are specifically set by the sending code. This is because if these are not set by the sending code, then the HTTP library adds these headers at a lower level as it is sending the headers and thus res.getHeaders() does not retrieve them and never knows about them.
Edit: I overlooked your first screenshot... Are you using any middleware? It looks like you're using the CORS middleware, at least - which is why you are showing more headers than the defaults..
It looks like Node/Express sends a Content-Length header when it can..
The Date header is mandatory per the HTTP spec, but it looks like you can change that behavior in Node/Express - I'm guessing by default Node/Express sets that value to true.
I did test setting res.sendDate = false and the date header was not sent, so it looks like that header is set by default for you, most likely as the last step in the response?
With res.sendDate = false;
Without setting res.sendDate (aka the default):
All in all, I'm assuming the headers you don't see when you console.log(res.getHeaders()) are set by Node/Express by default..
I wasn't able to find anything in the docs about default response headers (outside of the Date header), but it's possible I overlooked something. The Express docs don't have anything on it as they just use the built in http module from Node.
I'm using Express to to serve some webpages. I have a font file (woff) that I'm downloading using node's https API. I then pass the response headers along to the response I'm returning to the client. The issue I have is that express seems to be modifying the response headers, specifically the content-type and content-length headers.
I'm calling res.set() and passing the headers from the server side request
Here is some code:
app.get('/*', (req, res, next) => {
https.get(URI, (serversideRes) => {
serversideRes.on('end', () => {
res.set(serversideRes.headers);
console.log(res.getHeaders()['content-type']); //font/x-woff
console.log(res.getHeaders()['content-length']); //27756
res.send(data);
console.log(res.getHeaders()['content-type']); //font/x-woff; charset=utf-8
console.log(res.getHeaders()['content-length']); //49574
}
}
}
In the browser console I'm getting
Failed to decode downloaded font
OTS parsing error: incorrect file size in WOFF header
From the Express API
This method performs many useful tasks for simple non-streaming responses: For example, it automatically assigns the Content-Length HTTP response header field (unless previously defined) and provides automatic HEAD and HTTP cache freshness support.
In this case, the headers clearly seem to be previously defined.
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.
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
What happens in NodeJS if I call response.end() as soon as I receive a request without supplying any data, calling response.write(), or calling response.writeHead()? I'm curious about the answer from two angles:
1) the server angle - does this essentially take something off of a backend stack or heap within nodeJS that frees up some resources? or does something entirely different happen?
2) the client perspective - If this is coming from an ajax request that doesn't expect to receive any data in response, can any problems arise?
The obvious thing to do here, is just to try it. Node.js code:
var http = require('http');
http.createServer(function (req, res) {
res.end();
}).listen(1337, '127.0.0.1');
From the server perspective it's not different to adding data / custom headers in there.
From client perspective, gives me empty response body, with headers:
Connection:keep-alive
Date:Sun, 29 Jul 2012 07:58:51 GMT
Transfer-Encoding:chunked
The difference to the hello world example is that Content-Type header is missing and the response body is empty and that's it.
1) All resources associated with the request and response are relinquished (made available for garbage collection)
2) node sends out a response with default headers and an empty body. It's purely up to the client how to deal with that. In most cases, the client will regard the default headers as incomplete, but in the absence of a response body, there's no alternative.