Consider the following HTTP client, following official example of the http
module`:
var http = require('http');
http.get('http://127.0.0.1:12345/', function(response) {
console.log('got response ', response.statusCode);
var body = [];
response.on('data', function(data) {
console.log('got data: ', data);
});
response.on('end', function() {
body = Buffer.concat(body).toString();
console.log('end body = ', body, ' complete = ', response.complete);
console.log('success!');
});
}).on('error', function(err) {
console.log('ClientRequest error: ' + err);
console.log('error!');
});
Here I make an HTTP request, and report on three occasions: whenever I start receiving an HTTP response, whenever I get some HTTP body data, whenever the response is complete, and on all errors.
I expect this code to always complete (unless the connection hangs) with exactly one of success!
or error!
in logs. However, if the connect resets after the server has already sent some headers, I get both. Here is an example output:
got response 200
got data: <Buffer 48 65 6c 6c 6f 0a>
ClientRequest error: Error: read ECONNRESET
error!
end body = Hello
complete = true
success!
How to reproduce:
nc -l -p 12345
to start a debug server.- Run the client with
node a.js
. I usenode v18.14.2
on Windows, but I saw the same behavior on Ubuntu 22.04 withnode v12.22.9
as well. - Type the following response, but do not abort
nc
just yet:HTTP/1.1 200 OK Hello
- Terminate the
nc
process with task manager (Windows) or kill the TCP connection withsudo tcpkill -i lo port 12345
and writing something else tonc
(Linux).
It seems like Node both detects the error (hence the error
callback is fired) and considers the HTTP request a success (hence to end
callback is fired). In my original code that resulted in two instances of my application continuing instead of one which was very confusing.
How do I make sure exactly one of those is fired, and only on successful HTTP responses?