Skip to content

Commit f8a4e16

Browse files
author
laino
committed
Fix #1047
1 parent 0a7bf81 commit f8a4e16

File tree

3 files changed

+52
-10
lines changed

3 files changed

+52
-10
lines changed

lib/_http_outgoing.js

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,24 @@ function OutgoingMessage() {
7171
this._header = null;
7272
this._headers = null;
7373
this._headerNames = {};
74+
75+
// Call remaining callbacks if stream closes prematurely
76+
this.once('close', function() {
77+
if (this.output.length === 0) return;
78+
79+
const err = new Error('stream closed prematurely');
80+
const outputCallbacks = this.outputCallbacks;
81+
82+
this.output = [];
83+
this.outputEncodings = [];
84+
this.outputCallbacks = [];
85+
86+
for (var i = 0; i < outputCallbacks.length; i++) {
87+
let callback = outputCallbacks[i];
88+
if (callback)
89+
callback(err);
90+
}
91+
});
7492
}
7593
util.inherits(OutgoingMessage, Stream);
7694

@@ -162,12 +180,8 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
162180

163181
// Directly write to socket.
164182
return connection.write(data, encoding, callback);
165-
} else if (connection && connection.destroyed) {
166-
// The socket was destroyed. If we're still trying to write to it,
167-
// then we haven't gotten the 'close' event yet.
168-
return false;
169183
} else {
170-
// buffer, as long as we're not destroyed.
184+
// buffer, as long as we didn't get the "close" event
171185
return this._buffer(data, encoding, callback);
172186
}
173187
};
@@ -430,10 +444,13 @@ OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
430444
throw new TypeError('first argument must be a string or Buffer');
431445
}
432446

433-
434447
// If we get an empty string or buffer, then just do nothing, and
435448
// signal the user to keep writing.
436-
if (chunk.length === 0) return true;
449+
if (chunk.length === 0) {
450+
if (typeof callback === 'function')
451+
process.nextTick(callback);
452+
return true;
453+
}
437454

438455
var len, ret;
439456
if (this.chunkedEncoding) {
@@ -517,11 +534,26 @@ OutgoingMessage.prototype.end = function(data, encoding, callback) {
517534
throw new TypeError('first argument must be a string or Buffer');
518535
}
519536

537+
var self = this;
538+
520539
if (this.finished) {
540+
if (data && data.length > 0) {
541+
// Report that writing the data has failed
542+
// because the stream was already 'ended'.
543+
var err = new Error('write after end');
544+
process.nextTick(function() {
545+
self.emit('error', err);
546+
if (callback) callback(err);
547+
});
548+
} else {
549+
// The user wanted to end the stream anyway,
550+
// so we don't need to report a failure.
551+
if (callback)
552+
process.nextTick(callback);
553+
}
521554
return false;
522555
}
523556

524-
var self = this;
525557
function finish() {
526558
self.emit('finish');
527559
}

test/parallel/test-http-destroyed-socket-write2.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ server.listen(common.PORT, function() {
4040
var sawEnd = false;
4141

4242
req.on('error', function(er) {
43-
assert(!gotError);
43+
44+
// Each failed write will cause an error, but
45+
// we are only interested in one
46+
if (gotError) return;
47+
4448
gotError = true;
4549
switch (er.code) {
4650
// This is the expected case

test/parallel/test-http-many-ended-pipelines.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ var numRequests = 20;
1616
var done = 0;
1717

1818
var server = http.createServer(function(req, res) {
19-
res.end('ok');
19+
20+
res.end('ok', common.mustCall(function() {}));
21+
22+
// We *might* get a socket already closed error here, which
23+
// occurs when the socket was destroyed before we finished
24+
// writing our data.
25+
res.on('error', function() {});
2026

2127
// Oh no! The connection died!
2228
req.socket.destroy();

0 commit comments

Comments
 (0)