Skip to content

Commit 0dcc433

Browse files
author
Igor Zinkovsky
committed
don't crash when queued write fails
1 parent d227084 commit 0dcc433

File tree

2 files changed

+52
-26
lines changed

2 files changed

+52
-26
lines changed

lib/net.js

+43-19
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ function initSocketHandle(self) {
8484
self._flags = 0;
8585
self._connectQueueSize = 0;
8686
self.destroyed = false;
87+
self.errorEmitted = false;
8788
self.bytesRead = 0;
8889
self.bytesWritten = 0;
8990

@@ -244,7 +245,7 @@ Socket.prototype.end = function(data, encoding) {
244245
var shutdownReq = this._handle.shutdown();
245246

246247
if (!shutdownReq) {
247-
this.destroy(errnoException(errno, 'shutdown'));
248+
this._destroy(errnoException(errno, 'shutdown'));
248249
return false;
249250
}
250251

@@ -267,7 +268,7 @@ function afterShutdown(status, handle, req) {
267268
}
268269

269270
if (self._flags & FLAG_GOT_EOF || !self.readable) {
270-
self.destroy();
271+
self._destroy();
271272
} else {
272273
}
273274
}
@@ -278,7 +279,7 @@ Socket.prototype.destroySoon = function() {
278279
this._flags |= FLAG_DESTROY_SOON;
279280

280281
if (this._pendingWriteReqs == 0) {
281-
this.destroy();
282+
this._destroy();
282283
}
283284
};
284285

@@ -290,11 +291,24 @@ Socket.prototype._connectQueueCleanUp = function(exception) {
290291
};
291292

292293

293-
Socket.prototype.destroy = function(exception) {
294-
if (this.destroyed) return;
295-
294+
Socket.prototype._destroy = function(exception, cb) {
296295
var self = this;
297296

297+
function fireErrorCallbacks() {
298+
if (cb) cb(exception);
299+
if (exception && !self.errorEmitted) {
300+
process.nextTick(function() {
301+
self.emit('error', exception);
302+
});
303+
self.errorEmitted = true;
304+
}
305+
};
306+
307+
if (this.destroyed) {
308+
fireErrorCallbacks();
309+
return;
310+
}
311+
298312
self._connectQueueCleanUp();
299313

300314
debug('destroy');
@@ -315,15 +329,21 @@ Socket.prototype.destroy = function(exception) {
315329
this._handle = null;
316330
}
317331

332+
fireErrorCallbacks();
333+
318334
process.nextTick(function() {
319-
if (exception) self.emit('error', exception);
320335
self.emit('close', exception ? true : false);
321336
});
322337

323338
this.destroyed = true;
324339
};
325340

326341

342+
Socket.prototype.destroy = function(exception) {
343+
this._destroy(exception);
344+
}
345+
346+
327347
function onread(buffer, offset, length) {
328348
var handle = this;
329349
var self = handle.socket;
@@ -362,17 +382,17 @@ function onread(buffer, offset, length) {
362382

363383
// We call destroy() before end(). 'close' not emitted until nextTick so
364384
// the 'end' event will come first as required.
365-
if (!self.writable) self.destroy();
385+
if (!self.writable) self._destroy();
366386

367387
if (!self.allowHalfOpen) self.end();
368388
if (self._events && self._events['end']) self.emit('end');
369389
if (self.onend) self.onend();
370390
} else {
371391
// Error
372392
if (errno == 'ECONNRESET') {
373-
self.destroy();
393+
self._destroy();
374394
} else {
375-
self.destroy(errnoException(errno, 'read'));
395+
self._destroy(errnoException(errno, 'read'));
376396
}
377397
}
378398
}
@@ -450,13 +470,16 @@ Socket.prototype.write = function(data, arg1, arg2) {
450470
Socket.prototype._write = function(data, encoding, cb) {
451471
timers.active(this);
452472

453-
if (!this._handle) throw new Error('This socket is closed.');
473+
if (!this._handle) {
474+
this._destroy(new Error('This socket is closed.'), cb);
475+
return false;
476+
}
454477

455478
// `encoding` is unused right now, `data` is always a buffer.
456479
var writeReq = this._handle.write(data);
457480

458481
if (!writeReq) {
459-
this.destroy(errnoException(errno, 'write'));
482+
this._destroy(errnoException(errno, 'write'), cb);
460483
return false;
461484
}
462485

@@ -477,7 +500,7 @@ function afterWrite(status, handle, req, buffer) {
477500
}
478501

479502
if (status) {
480-
self.destroy(errnoException(errno, 'write'));
503+
self._destroy(errnoException(errno, 'write'), req.cb);
481504
return;
482505
}
483506

@@ -494,7 +517,7 @@ function afterWrite(status, handle, req, buffer) {
494517
if (req.cb) req.cb();
495518

496519
if (self._pendingWriteReqs == 0 && self._flags & FLAG_DESTROY_SOON) {
497-
self.destroy();
520+
self._destroy();
498521
}
499522
}
500523

@@ -522,7 +545,7 @@ function connect(self, address, port, addressType) {
522545
if (connectReq !== null) {
523546
connectReq.oncomplete = afterConnect;
524547
} else {
525-
self.destroy(errnoException(errno, 'connect'));
548+
self._destroy(errnoException(errno, 'connect'));
526549
}
527550
}
528551

@@ -570,7 +593,7 @@ Socket.prototype.connect = function(port /* [host], [cb] */) {
570593
// error event to the next tick.
571594
process.nextTick(function() {
572595
self.emit('error', err);
573-
self.destroy();
596+
self._destroy();
574597
});
575598
} else {
576599
timers.active(self);
@@ -619,8 +642,9 @@ function afterConnect(status, handle, req, readable, writable) {
619642

620643
if (self._connectQueue) {
621644
debug('Drain the connect queue');
622-
for (var i = 0; i < self._connectQueue.length; i++) {
623-
self._write.apply(self, self._connectQueue[i]);
645+
var connectQueue = self._connectQueue;
646+
for (var i = 0; i < connectQueue.length; i++) {
647+
self._write.apply(self, connectQueue[i]);
624648
}
625649
self._connectQueueCleanUp();
626650
}
@@ -634,7 +658,7 @@ function afterConnect(status, handle, req, readable, writable) {
634658
}
635659
} else {
636660
self._connectQueueCleanUp();
637-
self.destroy(errnoException(errno, 'connect'));
661+
self._destroy(errnoException(errno, 'connect'));
638662
}
639663
}
640664

test/simple/test-net-write-after-close.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,23 @@ var assert = require('assert');
2424
var net = require('net');
2525

2626
var gotError = false;
27+
var gotWriteCB = false;
2728

2829
process.on('exit', function() {
2930
assert(gotError);
31+
assert(gotWriteCB);
3032
});
3133

3234
var server = net.createServer(function(socket) {
33-
setTimeout(function() {
34-
assert.throws(
35-
function() {
36-
socket.write('test');
37-
},
38-
/This socket is closed/
39-
);
35+
socket.on('error', function(error) {
4036
server.close();
4137
gotError = true;
38+
});
39+
40+
setTimeout(function() {
41+
socket.write('test', function(e) {
42+
gotWriteCB = true;
43+
});
4244
}, 250);
4345
});
4446

0 commit comments

Comments
 (0)