-
-
Notifications
You must be signed in to change notification settings - Fork 34.2k
Description
- Version: 7.8.0 (also affects 4 & 6)
- Platform: macOS 10.12.4
- Subsystem: http / net
When a server responds with a short payload to a POST http.request() with a large payload that has not finished uploading, a node client throws an EPIPE error on the request object. E.g. with NODE_DEBUG=http:
HTTP 90220: SOCKET ERROR: write EPIPE Error: write EPIPE
at exports._errnoException (util.js:1034:11)
at WriteWrap.afterWrite [as oncomplete] (net.js:812:14)
This destroys the node socket, and the http client never receives the response or any of the payload data.
Expected result
The EPIPE error should be deferred (or possibly just ignored) until the client has had a chance to handle the response and payload.
Serverside workaround
This can be mitigated serverside, by consuming all the uploaded data before sending a response.
Background
I need clients to be able to handle my server responding to POST requests without consuming the entire payload.
Example
I have created a failing example, which when run will trigger the EPIPE error.
To visualise that this is indeed a node bug, I added an ignore_epipe option, which can be set to true, to ignore EPIPE errors on the socket. This results in the client behaving as I expected, properly delivering the response and payload, before emitting an error.
var http = require('http');
var port = 8080;
var ignore_epipe = false;
var upload = function () {
var req = http.request({
port: port,
method: 'POST'
}, function (res) {
console.log('received response code', res.statusCode);
var length = 0
res.setEncoding('utf8');
res.on('data', function (chunk) {
//console.log('BODY: ' + chunk);
length += chunk.length;
});
res.on('end', function () {
console.log('received ' + length + ' bytes');
});
res.on('error', function (err) {
console.log('got response error', err);
});
});
req.end(new Buffer(10*1024*1024));
req.on('error', function (err) {
console.log('got request error', err);
});
if (ignore_epipe) {
req.on('socket', function (socket) {
var destroy = socket._destroy;
socket._destroy = function (exception, cb) {
if (exception && (exception.code === 'EPIPE')) {
return;
}
return destroy.call(this, exception, cb);
};
});
}
};
// create server
var server = http.createServer(function (req, res) {
res.end('done');
});
server.listen(port, function (err) {
if (err) {
return console.log('something bad happened', err)
}
console.log('server is listening on', port);
upload();
});