I write a simple server with node.js to parsing the request body. I execute the req.end() before parsing the request body. It shouldn't work, but it actually does work nicely. How come? Here's my code:
import http from "node:http";
import fs from "node:fs"
const server = http.createServer((req, res) => {
const {url , method } = req;
if(url === '/'){
res.setHeader("content-type", "text/html");
res.write("<!DOCTYPE html>");
res.write("<html>");
res.write("<head><title>Enter Message</title></head>");
res.write('<body><form action="/message" method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>');
res.write("</html>");
return res.end();
}
if(url === '/message' && method === 'POST'){
const body = [];
req.on('data', (chunk) => {
console.log(chunk);
body.push(chunk);
})
req.on('end', () => {
const parsedBody = Buffer.concat(body).toString();
const message = parsedBody.split("=")[1];
fs.writeFileSync("message.txt", message);
})
res.statusCode = 302;
res.setHeader('Location', '/');
// this code run first
return res.end();
}
});
const port = 4000;
server.listen(port, () => {
console.log(`The server is listening on http://127.0.0.1:${port}`);
});
HTTP is based on TCP, which allows connections to be in a half-closed state (from either end), where one half of the duplex stream is closed but the other isn't. It means that no more data will be written, but still more data may be received.
Using this is a bad idea, though, as discussed in https://github.com/httpwg/http-core/issues/22. It also doesn't work across some NATs apparently. Does HTTP/1.1 allow delimiting end of request by half-closing the connection? and Conformant HTTP 1.1 Server and client-side connection half-close also discuss whether a client should be allowed to close its write stream before finishing to read the response.
However, that's not what node's HttpResponse.end
method (docs) actually does. It really just marks the end of the response and will prevent anyone from writing further data. It does not close the socket, which might need to be reused for pipelined requests.
A better reason for why this is a bad idea on the server is that you should validate the entire request message before sending any response, as there might be errors that need to be reported in the status code. Responding with 302 to anything that is thrown at you is not appropriate.